Bug 782352 - Broadcast local io to onchange listeners. r=sicking
authorDoug Turner <dougt@dougt.org>
Fri, 17 Aug 2012 19:43:00 -0700
changeset 102705 77d2640d9892851b26fe4f531b6cb58f19d1a0bc
parent 102704 c7934a5efa2cb5e5994c86eb893a55f5f13e39dc
child 102706 13efbdf35a8b713dd536208e97228c4b3703e2a6
push id23303
push userryanvm@gmail.com
push dateSat, 18 Aug 2012 11:22:19 +0000
treeherdermozilla-central@9c48df21d744 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssicking
bugs782352
milestone17.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 782352 - Broadcast local io to onchange listeners. r=sicking
dom/devicestorage/DeviceStorage.h
dom/devicestorage/DeviceStorageRequestParent.cpp
dom/devicestorage/nsDeviceStorage.cpp
dom/devicestorage/nsDeviceStorage.h
dom/devicestorage/test/test_watch.html
dom/ipc/ContentChild.cpp
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
--- a/dom/devicestorage/DeviceStorage.h
+++ b/dom/devicestorage/DeviceStorage.h
@@ -8,25 +8,23 @@
 #include "nsIDOMDeviceStorage.h"
 #include "nsIFile.h"
 #include "nsIPrincipal.h"
 #include "nsIObserver.h"
 #include "nsDOMEventTargetHelper.h"
 
 class nsDOMDeviceStorage MOZ_FINAL
   : public nsIDOMDeviceStorage
-  , public nsIFileUpdateListener
   , public nsDOMEventTargetHelper
   , public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOMDEVICESTORAGE
 
-  NS_DECL_NSIFILEUPDATELISTENER
   NS_DECL_NSIOBSERVER
   NS_DECL_NSIDOMEVENTTARGET
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsDOMDeviceStorage, nsDOMEventTargetHelper)
   NS_DECL_EVENT_HANDLER(change)
 
   nsDOMDeviceStorage();
 
   nsresult Init(nsPIDOMWindow* aWindow, const nsAString &aType);
@@ -53,21 +51,23 @@ private:
                              bool aEditable, 
                              nsIDOMDeviceStorageCursor** aRetval);
 
   PRInt32 mStorageType;
   nsCOMPtr<nsIFile> mFile;
 
   nsCOMPtr<nsIPrincipal> mPrincipal;
 
+  bool mIsWatchingFile;
+
+  nsresult Notify(const char* aReason, nsIFile* aFile);
+
   friend class WatchFileEvent;
   friend class DeviceStorageRequest;
 
-  bool  mIsWatchingFile;
-
 #ifdef MOZ_WIDGET_GONK
   void DispatchMountChangeEvent(nsAString& aType);
 #endif
 
   // nsIDOMDeviceStorage.type
   enum {
       DEVICE_STORAGE_TYPE_DEFAULT = 0,
       DEVICE_STORAGE_TYPE_SHARED,
--- a/dom/devicestorage/DeviceStorageRequestParent.cpp
+++ b/dom/devicestorage/DeviceStorageRequestParent.cpp
@@ -269,17 +269,17 @@ DeviceStorageRequestParent::DeleteFileEv
 {
 }
 
 nsresult
 DeviceStorageRequestParent::DeleteFileEvent::CancelableRun()
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
-  mFile->mFile->Remove(true);
+  mFile->Remove();
 
   nsRefPtr<nsRunnable> r;
 
   bool check = false;
   mFile->mFile->Exists(&check);
   if (check) {
     r = new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN);
   }
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -45,16 +45,42 @@
 #include "nsIVolumeService.h"
 #endif
 
 using namespace mozilla::dom;
 using namespace mozilla::dom::devicestorage;
 
 #include "nsDirectoryServiceDefs.h"
 
+class IOEventComplete : public nsRunnable
+{
+public:
+  IOEventComplete(nsIFile *aFile, const char *aType)
+    : mFile(aFile)
+    , mType(aType)
+  {
+  }
+
+  ~IOEventComplete() {}
+
+  NS_IMETHOD Run()
+  {
+    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+    nsString data;
+    CopyASCIItoUTF16(mType, data);
+    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+    obs->NotifyObservers(mFile, "file-watcher-update", data.get());    
+    return NS_OK;
+  }
+
+private:
+  nsCOMPtr<nsIFile> mFile;
+  nsCString mType;
+};
+
 DeviceStorageFile::DeviceStorageFile(nsIFile* aFile, const nsAString& aPath)
   : mPath(aPath)
   , mEditable(false)
 {
   NS_ASSERTION(aFile, "Must not create a DeviceStorageFile with a null nsIFile");
   // always take a clone
   nsCOMPtr<nsIFile> file;
   aFile->Clone(getter_AddRefs(mFile));
@@ -154,16 +180,19 @@ DeviceStorageFile::Write(nsIInputStream*
     return NS_ERROR_FAILURE;
   }
 
   nsresult rv = mFile->Create(nsIFile::NORMAL_FILE_TYPE, 00600);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
+  nsCOMPtr<IOEventComplete> iocomplete = new IOEventComplete(mFile, "created");
+  NS_DispatchToMainThread(iocomplete);    
+
   PRUint64 bufSize = 0;
   aInputStream->Available(&bufSize);
 
   nsCOMPtr<nsIOutputStream> outputStream;
   NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), mFile);
 
   if (!outputStream) {
     return NS_ERROR_FAILURE;
@@ -176,56 +205,76 @@ DeviceStorageFile::Write(nsIInputStream*
 
   if (!bufferedOutputStream) {
     return NS_ERROR_FAILURE;
   }
 
   rv = NS_OK;
   while (bufSize) {
     PRUint32 wrote;
-    rv = bufferedOutputStream->WriteFrom(aInputStream, static_cast<PRUint32>(NS_MIN<PRUint64>(bufSize, PR_UINT32_MAX)), &wrote);
+    rv = bufferedOutputStream->WriteFrom(aInputStream,
+					 static_cast<PRUint32>(NS_MIN<PRUint64>(bufSize, PR_UINT32_MAX)),
+					 &wrote);
     if (NS_FAILED(rv)) {
       break;
     }
     bufSize -= wrote;
   }
 
+  iocomplete = new IOEventComplete(mFile, "modified");
+  NS_DispatchToMainThread(iocomplete);
+
   bufferedOutputStream->Close();
   outputStream->Close();
   if (NS_FAILED(rv)) {
     return rv;
   }
   return NS_OK;
 }
 
 nsresult
 DeviceStorageFile::Write(InfallibleTArray<PRUint8>& aBits) {
 
   nsresult rv = mFile->Create(nsIFile::NORMAL_FILE_TYPE, 00600);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
+  nsCOMPtr<IOEventComplete> iocomplete = new IOEventComplete(mFile, "created");
+  NS_DispatchToMainThread(iocomplete);
+
   nsCOMPtr<nsIOutputStream> outputStream;
   NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), mFile);
 
   if (!outputStream) {
     return NS_ERROR_FAILURE;
   }
 
   PRUint32 wrote;
   outputStream->Write((char*) aBits.Elements(), aBits.Length(), &wrote);
   outputStream->Close();
 
+  iocomplete = new IOEventComplete(mFile, "modified");
+  NS_DispatchToMainThread(iocomplete);
+
   if (aBits.Length() != wrote) {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
+nsresult
+DeviceStorageFile::Remove()
+{
+  mFile->Remove(true);
+  nsCOMPtr<IOEventComplete> iocomplete = new IOEventComplete(mFile, "deleted");
+  NS_DispatchToMainThread(iocomplete);    
+  return NS_OK;
+}
+
 void
 DeviceStorageFile::CollectFiles(nsTArray<nsRefPtr<DeviceStorageFile> > &aFiles,
                                 PRUint64 aSince)
 {
   nsString rootPath;
   nsresult rv = mFile->GetPath(rootPath);
   if (NS_FAILED(rv)) {
     return;
@@ -442,16 +491,19 @@ nsDOMDeviceStorage::SetRootFileForType(c
         f->Normalize();
       }
     }
   } 
 
 #ifdef MOZ_WIDGET_GONK
   RegisterForSDCardChanges(this);
 #endif
+
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  obs->AddObserver(this, "file-watcher-update", false);
   mFile = f;
 }
 
 jsval InterfaceToJsval(nsPIDOMWindow* aWindow, nsISupports* aObject, const nsIID* aIID)
 {
   nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aWindow);
   if (!sgo) {
     return JSVAL_NULL;
@@ -892,17 +944,16 @@ public:
   }
 
 private:
   PRInt64 mFreeBytes, mTotalBytes;
   nsString mState;
   nsRefPtr<DOMRequest> mRequest;
 };
 
-
 class PostResultEvent : public nsRunnable
 {
 public:
   PostResultEvent(nsRefPtr<DOMRequest>& aRequest, DeviceStorageFile* aFile)
     : mFile(aFile)
     {
       mRequest.swap(aRequest);
     }
@@ -965,28 +1016,27 @@ public:
 
       nsCOMPtr<PostErrorEvent> event = new PostErrorEvent(mRequest,
                                                           POST_ERROR_EVENT_UNKNOWN,
                                                           mFile);
       NS_DispatchToMainThread(event);
       return NS_OK;
     }
 
-    nsCOMPtr<PostResultEvent> event = new PostResultEvent(mRequest,
-                                                          mFile->mPath);
+    nsCOMPtr<PostResultEvent> event = new PostResultEvent(mRequest, mFile->mPath);
     NS_DispatchToMainThread(event);
-
     return NS_OK;
   }
 
 private:
   nsCOMPtr<nsIDOMBlob> mBlob;
   nsRefPtr<DeviceStorageFile> mFile;
   nsRefPtr<DOMRequest> mRequest;
 };
+
 class ReadFileEvent : public nsRunnable
 {
 public:
     ReadFileEvent(DeviceStorageFile* aFile,
                   nsRefPtr<DOMRequest>& aRequest)
   : mFile(aFile)
     {
       mRequest.swap(aRequest);
@@ -1029,18 +1079,17 @@ public:
       mRequest.swap(aRequest);
     }
 
   ~DeleteFileEvent() {}
 
   NS_IMETHOD Run()
   {
     NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
-
-    mFile->mFile->Remove(true);
+    mFile->Remove();
 
     nsRefPtr<nsRunnable> r;
 
     bool check = false;
     mFile->mFile->Exists(&check);
     if (check) {
       r = new PostErrorEvent(mRequest, POST_ERROR_EVENT_FILE_DOES_NOT_EXIST, mFile);
     }
@@ -1278,32 +1327,17 @@ public:
 	  return NS_OK;
         }
         r = new StatFileEvent(mFile, mRequest);
         break;
       }
 
       case DEVICE_STORAGE_REQUEST_WATCH:
       {
-         if (XRE_GetProcessType() != GeckoProcessType_Default) {
-           nsString fullpath;
-           mFile->mFile->GetPath(fullpath);
-           nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-           obs->AddObserver(mDeviceStorage, "file-watcher-update", false);
-           ContentChild::GetSingleton()->SendAddFileWatch(fullpath);
-         } else {
-           if (!mDeviceStorage->mIsWatchingFile) {
-
-             //TODO
-
-             mFile->mFile->Watch(mDeviceStorage);
-             mDeviceStorage->mIsWatchingFile = true;
-           }
-         }
-        return NS_OK;
+	// do something?
       }
     }
 
     if (r) {
       nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
       NS_ASSERTION(target, "Must have stream transport service");
       target->Dispatch(r, NS_DISPATCH_NORMAL);
     }
@@ -1373,17 +1407,16 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMDeviceStorage, nsDOMEventTargetHelper)
   NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(change)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 DOMCI_DATA(DeviceStorage, nsDOMDeviceStorage)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMDeviceStorage)
   NS_INTERFACE_MAP_ENTRY(nsIDOMDeviceStorage)
-  NS_INTERFACE_MAP_ENTRY(nsIFileUpdateListener)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(DeviceStorage)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(nsDOMDeviceStorage, nsDOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(nsDOMDeviceStorage, nsDOMEventTargetHelper)
 
 nsDOMDeviceStorage::nsDOMDeviceStorage()
@@ -1420,29 +1453,19 @@ nsDOMDeviceStorage::~nsDOMDeviceStorage(
 void
 nsDOMDeviceStorage::Shutdown()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
 #ifdef MOZ_WIDGET_GONK
   UnregisterForSDCardChanges(this);
 #endif
-  if (mIsWatchingFile) {
-    if (XRE_GetProcessType() != GeckoProcessType_Default) {
-      nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-      obs->RemoveObserver(this, "file-watcher-update");
 
-      nsString fullpath;
-      mFile->GetPath(fullpath);
-      ContentChild::GetSingleton()->SendRemoveFileWatch(fullpath);
-    }
-    else {
-      mFile->Unwatch(this);
-    }
-  }
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  obs->RemoveObserver(this, "file-watcher-update");
 }
 
 void
 nsDOMDeviceStorage::CreateDeviceStoragesFor(nsPIDOMWindow* aWin,
                                             const nsAString &aType,
                                             nsDOMDeviceStorage** aStore)
 {
   nsRefPtr<nsDOMDeviceStorage> storage = new nsDOMDeviceStorage();
@@ -1787,49 +1810,26 @@ nsDOMDeviceStorageStat::GetState(nsAStri
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorage::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *aData)
 {
   if (!strcmp(aTopic, "file-watcher-update")) {
 
-    // data strings will have the format of
-    //  reason:path
-    nsDependentString data(aData);
-
-    nsAString::const_iterator start, end;
-    nsAString::const_iterator colon;
-
-    data.BeginReading(start);
-    data.EndReading(end);
-    colon = end;
-
-    nsString reason;
-    nsString filepath;
-    if (!FindInReadable(NS_LITERAL_STRING(":"), start, colon)) {
+    nsCOMPtr<nsIFile> file = do_QueryInterface(aSubject);
+    if (!file) {
       return NS_OK;
     }
-   
-    filepath = Substring(colon, end);
-    data.BeginReading(start);
-    reason = Substring(start, --colon);
-
-    nsCOMPtr<nsIFile> f;
-    NS_NewLocalFile(filepath, false, getter_AddRefs(f));
- 
-    nsCString creason;
-    CopyUTF16toUTF8(reason, creason);
-
-    Update(creason.get(), f);
+    Notify(NS_ConvertUTF16toUTF8(aData).get(), file);
     return NS_OK;
   }
 
 #ifdef MOZ_WIDGET_GONK
-  if (!strcmp(aTopic, NS_VOLUME_STATE_CHANGED)) {
+  else if (!strcmp(aTopic, NS_VOLUME_STATE_CHANGED)) {
     nsCOMPtr<nsIVolume> vol = do_QueryInterface(aSubject);
     if (!vol) {
       return NS_OK;
     }
     nsString volName;
     vol->GetName(volName);
     if (!volName.Equals(NS_LITERAL_STRING("sdcard"))) {
       return NS_OK;
@@ -1854,19 +1854,23 @@ nsDOMDeviceStorage::Observe(nsISupports 
     }
     DispatchMountChangeEvent(type);
     return NS_OK;
   }
 #endif
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsDOMDeviceStorage::Update(const char* aReason, nsIFile* aFile)
+nsresult
+nsDOMDeviceStorage::Notify(const char* aReason, nsIFile* aFile)
 {
+  if (!mFile) {
+    return NS_ERROR_FAILURE;
+  }
+
   nsString rootpath;
   nsresult rv = mFile->GetPath(rootpath);
   if (NS_FAILED(rv)) {
     return NS_OK;
   }
   
   nsString fullpath;
   rv = aFile->GetPath(fullpath);
@@ -1921,37 +1925,36 @@ nsDOMDeviceStorage::AddEventListener(con
 
 NS_IMETHODIMP
 nsDOMDeviceStorage::AddSystemEventListener(const nsAString & aType,
                                            nsIDOMEventListener *aListener,
                                            bool aUseCapture,
                                            bool aWantsUntrusted,
                                            PRUint8 aArgc)
 {
+  if (!mIsWatchingFile) {
+    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+    obs->AddObserver(this, "file-watcher-update", false);
+    mIsWatchingFile = true;
+  }
+
   return nsDOMDeviceStorage::AddEventListener(aType,aListener,aUseCapture,aWantsUntrusted, aArgc);
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorage::RemoveEventListener(const nsAString & aType,
                                         nsIDOMEventListener *aListener,
                                         bool aUseCapture)
 {
   nsDOMEventTargetHelper::RemoveEventListener(aType, aListener, false);
 
   if (mIsWatchingFile && !HasListenersFor(NS_LITERAL_STRING("change"))) {
-    if (XRE_GetProcessType() != GeckoProcessType_Default) {
-      nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-      obs->RemoveObserver(this, "file-watcher-update");
-
-      nsString fullpath;
-      mFile->GetPath(fullpath);
-      ContentChild::GetSingleton()->SendRemoveFileWatch(fullpath);
-    } else {
-      mFile->Unwatch(this);
-    }
+    mIsWatchingFile = false;
+    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+    obs->RemoveObserver(this, "file-watcher-update");
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorage::RemoveSystemEventListener(const nsAString & aType,
                                               nsIDOMEventListener *aListener,
                                               bool aUseCapture)
--- a/dom/devicestorage/nsDeviceStorage.h
+++ b/dom/devicestorage/nsDeviceStorage.h
@@ -52,16 +52,17 @@ public:
   void SetEditable(bool aEditable);
 
   NS_DECL_ISUPPORTS
 
   // we want to make sure that the names of file can't reach
   // outside of the type of storage the user asked for.
   bool IsSafePath();
 
+  nsresult Remove();
   nsresult Write(nsIInputStream* aInputStream);
   nsresult Write(InfallibleTArray<PRUint8>& bits);
   void CollectFiles(nsTArray<nsRefPtr<DeviceStorageFile> > &aFiles, PRUint64 aSince = 0);
   void collectFilesInternal(nsTArray<nsRefPtr<DeviceStorageFile> > &aFiles, PRUint64 aSince, nsAString& aRootPath);
 
   static PRUint64 DirectoryDiskUsage(nsIFile* aFile, PRUint64 aSoFar = 0);
 
 private:
--- a/dom/devicestorage/test/test_watch.html
+++ b/dom/devicestorage/test/test_watch.html
@@ -31,17 +31,17 @@ function addSuccess(e) {
 
 function addError(e) {
   ok(false, "addError was called : " + e.target.error.name);
   devicestorage_cleanup();
 }
 
 function onChange(e) {
 
-  dump("we saw: " + e.path);
+  dump("we saw: " + e.path + " " + e.reason + "\n");
 
   if (e.path == gFileName) {
     ok(true, "we saw the file get created");
     storage.removeEventListener("change", onChange);
     devicestorage_cleanup();
   }
   else {
     // we may see other file changes during the test, and
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -942,25 +942,23 @@ ContentChild::RecvLastPrivateDocShellDes
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     obs->NotifyObservers(nullptr, "last-pb-context-exited", nullptr);
     return true;
 }
 
 bool
 ContentChild::RecvFilePathUpdate(const nsString& path, const nsCString& aReason)
 {
-    // data strings will have the format of
-    //  reason:path
-    nsString data;
-    CopyASCIItoUTF16(aReason, data);
-    data.Append(NS_LITERAL_STRING(":"));
-    data.Append(path);
+    nsCOMPtr<nsIFile> file;
+    NS_NewLocalFile(path, false, getter_AddRefs(file));
 
+    nsString reason;
+    CopyASCIItoUTF16(aReason, reason);
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-    obs->NotifyObservers(nullptr, "file-watcher-update", data.get());
+    obs->NotifyObservers(file, "file-watcher-update", reason.get());
     return true;
 }
 
 bool
 ContentChild::RecvFileSystemUpdate(const nsString& aFsName, const nsString& aName, const PRInt32 &aState)
 {
 #ifdef MOZ_WIDGET_GONK
     nsRefPtr<nsVolume> volume = new nsVolume(aFsName, aName, aState);
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -365,16 +365,17 @@ ContentParent::Init()
     if (obs) {
         obs->AddObserver(this, "xpcom-shutdown", false);
         obs->AddObserver(this, NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC, false);
         obs->AddObserver(this, "child-memory-reporter-request", false);
         obs->AddObserver(this, "memory-pressure", false);
         obs->AddObserver(this, "child-gc-request", false);
         obs->AddObserver(this, "child-cc-request", false);
         obs->AddObserver(this, "last-pb-context-exited", false);
+        obs->AddObserver(this, "file-watcher-update", false);
 #ifdef MOZ_WIDGET_GONK
         obs->AddObserver(this, NS_VOLUME_STATE_CHANGED, false);
 #endif
 #ifdef ACCESSIBILITY
         obs->AddObserver(this, "a11y-init-or-shutdown", false);
 #endif
     }
     Preferences::AddStrongObserver(this, "");
@@ -538,16 +539,17 @@ ContentParent::ActorDestroy(ActorDestroy
     if (obs) {
         obs->RemoveObserver(static_cast<nsIObserver*>(this), "xpcom-shutdown");
         obs->RemoveObserver(static_cast<nsIObserver*>(this), "memory-pressure");
         obs->RemoveObserver(static_cast<nsIObserver*>(this), "child-memory-reporter-request");
         obs->RemoveObserver(static_cast<nsIObserver*>(this), NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC);
         obs->RemoveObserver(static_cast<nsIObserver*>(this), "child-gc-request");
         obs->RemoveObserver(static_cast<nsIObserver*>(this), "child-cc-request");
         obs->RemoveObserver(static_cast<nsIObserver*>(this), "last-pb-context-exited");
+        obs->RemoveObserver(static_cast<nsIObserver*>(this), "file-watcher-update");
 #ifdef MOZ_WIDGET_GONK
         obs->RemoveObserver(static_cast<nsIObserver*>(this), NS_VOLUME_STATE_CHANGED);
 #endif
 #ifdef ACCESSIBILITY
         obs->RemoveObserver(static_cast<nsIObserver*>(this), "a11y-init-or-shutdown");
 #endif
     }
 
@@ -692,18 +694,16 @@ ContentParent::ContentParent(const nsASt
 
     if (gAppData) {
         nsCString version(gAppData->version);
         nsCString buildID(gAppData->buildID);
 
         //Sending all information to content process
         unused << SendAppInfo(version, buildID);
     }
-
-    mFileWatchers.Init();
 }
 
 ContentParent::~ContentParent()
 {
     if (OtherProcess())
         base::CloseProcessHandle(OtherProcess());
 
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
@@ -933,19 +933,16 @@ NS_IMPL_THREADSAFE_ISUPPORTS3(ContentPar
                               nsIDOMGeoPositionCallback)
 
 NS_IMETHODIMP
 ContentParent::Observe(nsISupports* aSubject,
                        const char* aTopic,
                        const PRUnichar* aData)
 {
     if (!strcmp(aTopic, "xpcom-shutdown") && mSubprocess) {
-
-        mFileWatchers.Clear();
-
         Close();
         NS_ASSERTION(!mSubprocess, "Close should have nulled mSubprocess");
     }
 
     if (!mIsAlive || !mSubprocess)
         return NS_OK;
 
     // listening for memory pressure event
@@ -990,16 +987,28 @@ ContentParent::Observe(nsISupports* aSub
         unused << SendGarbageCollect();
     }
     else if (!strcmp(aTopic, "child-cc-request")){
         unused << SendCycleCollect();
     }
     else if (!strcmp(aTopic, "last-pb-context-exited")) {
         unused << SendLastPrivateDocShellDestroyed();
     }
+    else if (!strcmp(aTopic, "file-watcher-update")) {
+        nsCString creason;
+        CopyUTF16toUTF8(aData, creason);
+        nsCOMPtr<nsIFile> file = do_QueryInterface(aSubject);
+        if (!file) {
+            return NS_OK;
+        }
+
+        nsString path;
+        file->GetPath(path);
+        unused << SendFilePathUpdate(path, creason);
+    }
 #ifdef MOZ_WIDGET_GONK
     else if(!strcmp(aTopic, NS_VOLUME_STATE_CHANGED)) {
         nsCOMPtr<nsIVolume> vol = do_QueryInterface(aSubject);
         if (!vol) {
             return NS_ERROR_NOT_AVAILABLE;
         }
 
         nsString volName;
@@ -1734,69 +1743,10 @@ ContentParent::RecvPrivateDocShellsExist
       obs->NotifyObservers(nullptr, "last-pb-context-exited", nullptr);
       delete gPrivateContent;
       gPrivateContent = NULL;
     }
   }
   return true;
 }
 
-bool
-ContentParent::RecvAddFileWatch(const nsString& root)
-{
-  nsRefPtr<WatchedFile> f;
-  if (mFileWatchers.Get(root, getter_AddRefs(f))) {
-    f->mUsageCount++;
-    return true;
-  }
-  
-  f = new WatchedFile(this, root);
-  mFileWatchers.Put(root, f);
-
-  f->Watch();
-  return true;
-}
-
-bool
-ContentParent::RecvRemoveFileWatch(const nsString& root)
-{
-  nsRefPtr<WatchedFile> f;
-  bool result = mFileWatchers.Get(root, getter_AddRefs(f));
-  if (!result) {
-    return true;
-  }
-
-  if (!f)
-    return true;
-
-  f->mUsageCount--;
-
-  if (f->mUsageCount > 0) {
-    return true;
-  }
-
-  f->Unwatch();
-  mFileWatchers.Remove(root);
-  return true;
-}
-
-NS_IMPL_ISUPPORTS1(ContentParent::WatchedFile, nsIFileUpdateListener)
-
-nsresult
-ContentParent::WatchedFile::Update(const char* aReason, nsIFile* aFile)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  nsString path;
-  aFile->GetPath(path);
-
-  unused << mParent->SendFilePathUpdate(path, nsDependentCString(aReason));
-
-#ifdef DEBUG
-  nsCString cpath;
-  aFile->GetNativePath(cpath);
-  printf("ContentParent::WatchedFile::Update: %s  -- %s\n", cpath.get(), aReason);
-#endif
-  return NS_OK;
-}
-
 } // namespace dom
 } // namespace mozilla
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -18,17 +18,16 @@
 #include "nsIObserver.h"
 #include "nsIThreadInternal.h"
 #include "nsNetUtil.h"
 #include "nsIPermissionManager.h"
 #include "nsIDOMGeoPositionCallback.h"
 #include "nsIMemoryReporter.h"
 #include "nsCOMArray.h"
 #include "nsDataHashtable.h"
-#include "nsInterfaceHashtable.h"
 #include "nsHashKeys.h"
 
 class mozIApplication;
 class nsFrameMessageManager;
 class nsIDOMBlob;
 
 namespace mozilla {
 
@@ -269,19 +268,16 @@ private:
                                  const nsString& aSourceLine,
                                  const PRUint32& aLineNumber,
                                  const PRUint32& aColNumber,
                                  const PRUint32& aFlags,
                                  const nsCString& aCategory);
 
     virtual bool RecvPrivateDocShellsExist(const bool& aExist);
 
-    virtual bool RecvAddFileWatch(const nsString& root);
-    virtual bool RecvRemoveFileWatch(const nsString& root);
-
     virtual void ProcessingError(Result what) MOZ_OVERRIDE;
 
     GeckoChildProcessHost* mSubprocess;
 
     PRInt32 mGeolocationWatchID;
     int mRunToCompletionDepth;
     bool mShouldCallUnblockChild;
 
@@ -292,43 +288,15 @@ private:
     nsCOMArray<nsIMemoryReporter> mMemoryReporters;
 
     bool mIsAlive;
     bool mSendPermissionUpdates;
 
     const nsString mAppManifestURL;
     nsRefPtr<nsFrameMessageManager> mMessageManager;
 
-    class WatchedFile MOZ_FINAL : public nsIFileUpdateListener {
-      public:
-        WatchedFile(ContentParent* aParent, const nsString& aPath)
-          : mParent(aParent)
-          , mUsageCount(1)
-        {
-          NS_NewLocalFile(aPath, false, getter_AddRefs(mFile));
-        }
-
-        NS_DECL_ISUPPORTS
-        NS_DECL_NSIFILEUPDATELISTENER
-
-        void Watch() {
-          mFile->Watch(this);
-        }
-
-        void Unwatch() {
-          mFile->Watch(this);
-        }
-
-        nsRefPtr<ContentParent> mParent;
-        PRInt32 mUsageCount;
-        nsCOMPtr<nsIFile> mFile;
-    };
-
-    // This is a cache of all of the registered file watchers.
-    nsInterfaceHashtable<nsStringHashKey, WatchedFile> mFileWatchers;
-
     friend class CrashReporterParent;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -293,17 +293,14 @@ parent:
         returns (PRUint8[] bits);
 
     sync GetShowPasswordSetting()
         returns (bool showPassword);
 
     // Notify the parent of the presence or absence of private docshells
     PrivateDocShellsExist(bool aExist);
 
-    AddFileWatch(nsString filepath);
-    RemoveFileWatch(nsString filepath);
-
 both:
      AsyncMessage(nsString aMessage, ClonedMessageData aData);
 };
 
 }
 }