Blob Blame History Raw
From 4ba7db7a91de431134c23bd2b43ca4e5e63e2bac Mon Sep 17 00:00:00 2001
From: Debarshi Ray <debarshir@gnome.org>
Date: Wed, 29 Mar 2017 16:04:08 +0200
Subject: [PATCH 01/16] documents: Simplify calculation of thumbnail paths for
 GoogleDocuments

https://bugzilla.gnome.org/show_bug.cgi?id=780718
---
 src/documents.js | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/src/documents.js b/src/documents.js
index 50cfbdafc7ee..2071882218b2 100644
--- a/src/documents.js
+++ b/src/documents.js
@@ -30,6 +30,7 @@ const GdPrivate = imports.gi.GdPrivate;
 const Gdk = imports.gi.Gdk;
 const GData = imports.gi.GData;
 const GLib = imports.gi.GLib;
+const GnomeDesktop = imports.gi.GnomeDesktop;
 const Gtk = imports.gi.Gtk;
 const Zpj = imports.gi.Zpj;
 const _ = imports.gettext.gettext;
@@ -916,12 +917,10 @@ const GoogleDocument = new Lang.Class({
                                                              authorization_domain: authorizationDomain,
                                                              download_uri: uri });
 
-                let checksum = new GLib.Checksum(GLib.ChecksumType.MD5);
-                checksum.update(this.uri, -1);
-                let dirPath = GLib.build_filenamev([GLib.get_user_cache_dir(), "thumbnails", "normal"]);
+                let path = GnomeDesktop.desktop_thumbnail_path_for_uri (this.uri,
+                                                                        GnomeDesktop.DesktopThumbnailSize.NORMAL);
+                let dirPath = GLib.path_get_dirname(path);
                 GLib.mkdir_with_parents(dirPath, 448);
-                let basename = checksum.get_string() + '.png';
-                let path = GLib.build_filenamev([dirPath, basename])
 
                 let downloadFile = Gio.File.new_for_path(path);
                 downloadFile.replace_async
-- 
2.9.4


From 7597a3e77b5c0192fc848f5fc7f65bb5b662ed20 Mon Sep 17 00:00:00 2001
From: Debarshi Ray <debarshir@gnome.org>
Date: Thu, 30 Mar 2017 16:54:15 +0200
Subject: [PATCH 02/16] documents: Track nfo:fileName for later use

Now that we can open ODFs and OOXMLs in LOKDocView, we don't need to
convert all remote documents to PDF. For example, we can open office
documents stored from OneDrive without any format conversion. The
nfo:fileName can be used to determine the extension of the cached file.

https://bugzilla.gnome.org/show_bug.cgi?id=774937
---
 src/documents.js | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/documents.js b/src/documents.js
index 2071882218b2..6ecc255c9407 100644
--- a/src/documents.js
+++ b/src/documents.js
@@ -229,6 +229,7 @@ const DocCommon = new Lang.Class({
     _init: function(cursor) {
         this.id = null;
         this.uri = null;
+        this.filename = null;
         this.name = null;
         this.author = null;
         this.mtime = null;
@@ -306,12 +307,12 @@ const DocCommon = new Lang.Class({
             this.uri = '';
 
         let title = cursor.get_string(Query.QueryColumns.TITLE)[0];
-        let filename = cursor.get_string(Query.QueryColumns.FILENAME)[0];
+        this.filename = cursor.get_string(Query.QueryColumns.FILENAME)[0];
 
         if (title && title != '')
             this.name = title;
-        else if (filename)
-            this.name = GdPrivate.filename_strip_extension(filename);
+        else if (this.filename)
+            this.name = GdPrivate.filename_strip_extension(this.filename);
         else
             this.name = '';
 
-- 
2.9.4


From 291a12b177411b960c91413a51f05493daf98bd1 Mon Sep 17 00:00:00 2001
From: Debarshi Ray <debarshir@gnome.org>
Date: Thu, 30 Mar 2017 17:25:44 +0200
Subject: [PATCH 03/16] documents: Add property to denote the URI to be loaded
 by the preview

We want to open ODFs and OOXMLs from OneDrive using LOKDocView, not
EvView. Therefore, the logic to convert a remote document into
something that can be loaded by the different preview widgets can no
longer be hidden inside GdPdfLoader. LOKDocView needs to know the URI
it can load without hitting the network.

For local and ownCloud this is the same as the actual URI because they
are natively handled by GIO. The property is not defined for Google
because it is still handled entirely by GdPdfLoader.

This changes the caching for SkydriveDocuments. The old scheme was:
  ~/.cache/gnome-documents/gnome-documents-<g_str_hash(id)>.pdf
The new structure is:
  ~/.cache/gnome-documents/skydrive/<SHA1(id)>.<original-extension>

The new scheme namespaces each document type and uses a hash function
that can be easily replicated by other tools for debugging. This is a
good time to change this because we are about to invalidate existing
caches by not using the ".pdf" extension for all SkydriveDocuments.

https://bugzilla.gnome.org/show_bug.cgi?id=774937
---
 src/documents.js | 24 +++++++++++++++++++++++-
 src/epubview.js  |  2 +-
 src/lokview.js   |  2 +-
 3 files changed, 25 insertions(+), 3 deletions(-)

diff --git a/src/documents.js b/src/documents.js
index 6ecc255c9407..c50d50078f2f 100644
--- a/src/documents.js
+++ b/src/documents.js
@@ -229,6 +229,7 @@ const DocCommon = new Lang.Class({
     _init: function(cursor) {
         this.id = null;
         this.uri = null;
+        this.uriToLoad = null;
         this.filename = null;
         this.name = null;
         this.author = null;
@@ -618,7 +619,7 @@ const DocCommon = new Lang.Class({
             return;
         }
 
-        GdPrivate.pdf_loader_load_uri_async(this.uri, passwd, cancellable, Lang.bind(this,
+        GdPrivate.pdf_loader_load_uri_async(this.uriToLoad, passwd, cancellable, Lang.bind(this,
             function(source, res) {
                 try {
                     let docModel = GdPrivate.pdf_loader_load_uri_finish(res);
@@ -749,6 +750,7 @@ const LocalDocument = new Lang.Class({
 
     populateFromCursor: function(cursor) {
         this.parent(cursor);
+        this.uriToLoad = this.uri;
 
         if (!Application.application.gettingStartedLocation)
             return;
@@ -1013,6 +1015,11 @@ const OwncloudDocument = new Lang.Class({
             this.defaultAppName = this.defaultApp.get_name();
     },
 
+    populateFromCursor: function(cursor) {
+        this.parent(cursor);
+        this.uriToLoad = this.uri;
+    },
+
     createThumbnail: function(callback) {
         GdPrivate.queue_thumbnail_job_for_file_async(this._file, Lang.bind(this,
             function(object, res) {
@@ -1071,6 +1078,21 @@ const SkydriveDocument = new Lang.Class({
         this.sourceName = _("OneDrive");
     },
 
+    populateFromCursor: function(cursor) {
+        this.parent(cursor);
+
+        let localDir = GLib.build_filenamev([GLib.get_user_cache_dir(), "gnome-documents", "skydrive"]);
+
+        let identifierHash = GLib.compute_checksum_for_string(GLib.ChecksumType.SHA1, this.identifier, -1);
+        let filenameStripped = GdPrivate.filename_strip_extension(this.filename);
+        let extension = this.filename.substring(filenameStripped.length);
+        let localFilename = identifierHash + extension;
+
+        let localPath = GLib.build_filenamev([localDir, localFilename]);
+        let localFile = Gio.File.new_for_path(localPath);
+        this.uriToLoad = localFile.get_uri();
+    },
+
     _createZpjEntry: function(cancellable, callback) {
         let source = Application.sourceManager.getItemById(this.resourceUrn);
 
diff --git a/src/epubview.js b/src/epubview.js
index 923b0aa265ab..12f392225d6f 100644
--- a/src/epubview.js
+++ b/src/epubview.js
@@ -65,7 +65,7 @@ const EPUBView = new Lang.Class({
     onLoadFinished: function(manager, doc) {
         this.parent(manager, doc);
 
-        let f = Gio.File.new_for_uri(doc.uri);
+        let f = Gio.File.new_for_uri(doc.uriToLoad);
         this._epubdoc = new Gepub.Doc({ path: f.get_path() });
         this._epubdoc.init(null);
 
diff --git a/src/lokview.js b/src/lokview.js
index 092adc5a0471..988db963d5d9 100644
--- a/src/lokview.js
+++ b/src/lokview.js
@@ -148,7 +148,7 @@ const LOKView = new Lang.Class({
         if (!isAvailable())
             return;
         this._doc = doc;
-        this._lokview.open_document(doc.uri, '{}', null, Lang.bind(this, this._onDocumentOpened));
+        this._lokview.open_document(doc.uriToLoad, '{}', null, Lang.bind(this, this._onDocumentOpened));
         this._progressBar.show();
     },
 
-- 
2.9.4


From 0ea69b0519a4e374feabc1923c82e1e0c71d5914 Mon Sep 17 00:00:00 2001
From: Debarshi Ray <debarshir@gnome.org>
Date: Thu, 30 Mar 2017 18:18:16 +0200
Subject: [PATCH 04/16] documents: Let everybody provide their own download
 implementation

This is meant to reduce our reliance on GdPdfLoader for loading remote
documents. We can open ODFs and OOXMLs in LOKDocView, so we don't need
to convert everything to PDFs. In the future, remote sub-classes will
provider their own implementation to download a document, and this will
help unify the loading across the various sub-classes.

https://bugzilla.gnome.org/show_bug.cgi?id=774937
---
 src/documents.js | 42 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/src/documents.js b/src/documents.js
index c50d50078f2f..9028e9b5084f 100644
--- a/src/documents.js
+++ b/src/documents.js
@@ -360,6 +360,48 @@ const DocCommon = new Lang.Class({
         }
     },
 
+    download: function(useCache, cancellable, callback) {
+        let localFile = Gio.File.new_for_uri(this.uriToLoad);
+        let localPath = localFile.get_path();
+        let localDir = GLib.path_get_dirname(localPath);
+        GLib.mkdir_with_parents(localDir, 448);
+
+        if (!useCache) {
+            this.downloadImpl(localFile, cancellable, callback);
+            return;
+        }
+
+        localFile.query_info_async(Gio.FILE_ATTRIBUTE_TIME_MODIFIED,
+                                   Gio.FileQueryInfoFlags.NONE,
+                                   GLib.PRIORITY_DEFAULT,
+                                   cancellable,
+                                   Lang.bind(this,
+            function(object, res) {
+                let info;
+
+                try {
+                    info = object.query_info_finish(res);
+                } catch (e) {
+                    this.downloadImpl(localFile, cancellable, callback);
+                    return;
+                }
+
+                let cacheMtime = info.get_attribute_uint64(Gio.FILE_ATTRIBUTE_TIME_MODIFIED);
+                cacheMtime /= 1000000;
+
+                if (this.mtime <= cacheMtime) {
+                    callback(true, null);
+                    return;
+                }
+
+                this.downloadImpl(localFile, cancellable, callback);
+            }));
+    },
+
+    downloadImpl: function(localFile, cancellable, callback) {
+        throw(new Error('DocCommon implementations must override downloadImpl'));
+    },
+
     load: function() {
         log('Error: DocCommon implementations must override load');
     },
-- 
2.9.4


From 630fc1a93bbd3a93ee242231a6a1129aef924127 Mon Sep 17 00:00:00 2001
From: Debarshi Ray <debarshir@gnome.org>
Date: Thu, 30 Mar 2017 18:23:27 +0200
Subject: [PATCH 05/16] documents: Implement the downloadImpl vfunc for
 SkydriveDocument

https://bugzilla.gnome.org/show_bug.cgi?id=774937
---
 src/documents.js | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 56 insertions(+)

diff --git a/src/documents.js b/src/documents.js
index 9028e9b5084f..781019091754 100644
--- a/src/documents.js
+++ b/src/documents.js
@@ -1161,6 +1161,62 @@ const SkydriveDocument = new Lang.Class({
                  }));
     },
 
+    downloadImpl: function(localFile, cancellable, callback) {
+        this._createZpjEntry(cancellable, Lang.bind(this,
+            function(entry, service, error) {
+                if (error) {
+                    callback(false, error);
+                    return;
+                }
+
+                service.download_file_to_stream_async(entry, cancellable, Lang.bind(this,
+                    function(object, res) {
+                        let inputStream;
+
+                        try {
+                            inputStream = object.download_file_to_stream_finish(res);
+                        } catch (e) {
+                            callback(false, e);
+                            return;
+                        }
+
+                        localFile.replace_async(null,
+                                                false,
+                                                Gio.FileCreateFlags.PRIVATE,
+                                                GLib.PRIORITY_DEFAULT,
+                                                cancellable,
+                                                Lang.bind(this,
+                            function(object, res) {
+                                let outputStream;
+
+                                try {
+                                    outputStream = object.replace_finish(res);
+                                } catch (e) {
+                                    callback(false, e);
+                                    return;
+                                }
+
+                                outputStream.splice_async(inputStream,
+                                                          Gio.OutputStreamSpliceFlags.CLOSE_SOURCE |
+                                                          Gio.OutputStreamSpliceFlags.CLOSE_TARGET,
+                                                          GLib.PRIORITY_DEFAULT,
+                                                          cancellable,
+                                                          Lang.bind(this,
+                                    function(object, res) {
+                                        try {
+                                            object.splice_finish(res);
+                                        } catch (e) {
+                                            callback(false, e);
+                                            return;
+                                        }
+
+                                        callback(false, null);
+                                    }));
+                            }));
+                    }));
+            }));
+    },
+
     load: function(passwd, cancellable, callback) {
         this._createZpjEntry(cancellable, Lang.bind(this,
             function(entry, service, exception) {
-- 
2.9.4


From c21f27f63ec2cf31c254896ad8343689b9cc9499 Mon Sep 17 00:00:00 2001
From: Debarshi Ray <debarshir@gnome.org>
Date: Thu, 30 Mar 2017 18:31:18 +0200
Subject: [PATCH 06/16] documents, pdf-loader: Fix previewing of ODFs and
 OOXMLs on OneDrive

OneDrive documents were being converted to PDFs via GdPdfLoader, but
LOKDocView was being used instead of EvView to render them. LOKDocView
uses the document's URI property, and in this case it is not a valid
URI that it can use. As a result the preview would fail and sometimes
crash the application.

Solve all that by removing the PDF conversion, and passing the correct
URI to LOKDocView.

This effectively reverts 0bfb23786dc84d390c4bd6bde5f20987874e1e9b

https://bugzilla.gnome.org/show_bug.cgi?id=774937
---
 configure.ac            |   4 +-
 src/documents.js        |  50 ++++++------
 src/lib/gd-pdf-loader.c | 205 +-----------------------------------------------
 src/lib/gd-pdf-loader.h |   9 ---
 4 files changed, 29 insertions(+), 239 deletions(-)

diff --git a/configure.ac b/configure.ac
index 78cda4f799d5..8098515028d1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -57,7 +57,6 @@ GOBJECT_INTROSPECTION_MIN_VERSION=1.31.6
 GDATA_MIN_VERSION=0.13.3
 GOA_MIN_VERSION=3.2.0
 TRACKER_MIN_VERSION=0.17.3
-ZAPOJIT_MIN_VERSION=0.0.2
 SOUP_MIN_VERSION=2.41.3
 
 AC_ARG_ENABLE(documentation,
@@ -85,8 +84,7 @@ PKG_CHECK_MODULES(DOCUMENTS,
                   tracker-control-1.0 >= $TRACKER_MIN_VERSION
                   tracker-sparql-1.0 >= $TRACKER_MIN_VERSION
                   goa-1.0 >= $GOA_MIN_VERSION
-                  libgdata >= $GDATA_MIN_VERSION
-                  zapojit-0.0 >= $ZAPOJIT_MIN_VERSION)
+                  libgdata >= $GDATA_MIN_VERSION)
 
 # Although GTK+ 3.10 includes hi-dpi functionality, it does not require a cairo with
 # cairo_surface_set_device_scale(), which we also need if we're to support hi-dpi,
diff --git a/src/documents.js b/src/documents.js
index 781019091754..ca48e53db25d 100644
--- a/src/documents.js
+++ b/src/documents.js
@@ -1218,34 +1218,36 @@ const SkydriveDocument = new Lang.Class({
     },
 
     load: function(passwd, cancellable, callback) {
-        this._createZpjEntry(cancellable, Lang.bind(this,
-            function(entry, service, exception) {
-                if (exception) {
-                    // try loading from the most recent cache, if any
-                    GdPrivate.pdf_loader_load_uri_async(this.identifier, passwd, cancellable, Lang.bind(this,
-                        function(source, res) {
-                            try {
-                                let docModel = GdPrivate.pdf_loader_load_uri_finish(res);
-                                callback(this, docModel, null);
-                            } catch (e) {
-                                // report the outmost error only
-                                callback(this, null, exception);
-                            }
-                        }));
-
+        this.download(true, cancellable, Lang.bind(this,
+            function(fromCache, error) {
+                if (error) {
+                    callback(this, null, error);
                     return;
                 }
 
-                GdPrivate.pdf_loader_load_zpj_entry_async
-                    (entry, service, cancellable, Lang.bind(this,
-                        function(source, res) {
-                            try {
-                                let docModel = GdPrivate.pdf_loader_load_zpj_entry_finish(res);
-                                callback(this, docModel, null);
-                            } catch (e) {
-                                callback(this, null, e);
+                this.loadLocal(passwd, cancellable, Lang.bind(this,
+                    function(doc, docModel, error) {
+                        if (error) {
+                            if (fromCache &&
+                                !error.matches(EvDocument.DocumentError, EvDocument.DocumentError.ENCRYPTED)) {
+                                this.download(false, cancellable, Lang.bind(this,
+                                    function(fromCache, error) {
+                                        if (error) {
+                                            callback(this, null, error);
+                                            return;
+                                        }
+
+                                        this.loadLocal(passwd, cancellable, callback);
+                                    }));
+                            } else {
+                                callback(this, null, error);
                             }
-                        }));
+
+                            return;
+                        }
+
+                        callback(this, docModel, null);
+                    }));
             }));
     },
 
diff --git a/src/lib/gd-pdf-loader.c b/src/lib/gd-pdf-loader.c
index 7a8890e75891..202f6cc0a65f 100644
--- a/src/lib/gd-pdf-loader.c
+++ b/src/lib/gd-pdf-loader.c
@@ -51,9 +51,6 @@ typedef struct {
   GDataService *gdata_service;
   gchar *resource_id;
 
-  ZpjSkydriveEntry *zpj_entry;
-  ZpjSkydrive *zpj_service;
-
   guint64 pdf_cache_mtime;
   guint64 original_file_mtime;
 
@@ -64,7 +61,6 @@ typedef struct {
 static void pdf_load_job_from_openoffice (PdfLoadJob *job);
 static void pdf_load_job_gdata_refresh_cache (PdfLoadJob *job);
 static void pdf_load_job_openoffice_refresh_cache (PdfLoadJob *job);
-static void pdf_load_job_zpj_refresh_cache (PdfLoadJob *job);
 
 /* --------------------------- utils -------------------------------- */
 
@@ -134,8 +130,6 @@ pdf_load_job_free (PdfLoadJob *job)
   g_clear_object (&job->download_file);
   g_clear_object (&job->gdata_service);
   g_clear_object (&job->gdata_entry);
-  g_clear_object (&job->zpj_service);
-  g_clear_object (&job->zpj_entry);
 
   g_free (job->uri);
   g_free (job->passwd);
@@ -160,7 +154,6 @@ static PdfLoadJob *
 pdf_load_job_new (GSimpleAsyncResult *result,
                   const gchar *uri,
                   GDataEntry *gdata_entry,
-                  ZpjSkydriveEntry *zpj_entry,
                   const gchar *passwd,
                   GCancellable *cancellable)
 {
@@ -178,8 +171,6 @@ pdf_load_job_new (GSimpleAsyncResult *result,
     retval->passwd = g_strdup (passwd);
   if (gdata_entry != NULL)
     retval->gdata_entry = g_object_ref (gdata_entry);
-  if (zpj_entry != NULL)
-    retval->zpj_entry = g_object_ref (zpj_entry);
   if (cancellable != NULL)
     retval->cancellable = g_object_ref (cancellable);
 
@@ -216,8 +207,6 @@ pdf_load_job_force_refresh_cache (PdfLoadJob *job)
 
   if (job->gdata_entry != NULL)
     pdf_load_job_gdata_refresh_cache (job);
-  if (job->zpj_entry != NULL)
-    pdf_load_job_zpj_refresh_cache (job);
   else
     pdf_load_job_openoffice_refresh_cache (job);
 }
@@ -429,61 +418,6 @@ pdf_load_job_gdata_refresh_cache (PdfLoadJob *job)
 }
 
 static void
-zpj_download_stream_ready (GObject *source,
-                       GAsyncResult *res,
-                       gpointer user_data)
-{
-  GError *error = NULL;
-  PdfLoadJob *job = (PdfLoadJob *) user_data;
-  const gchar *name;
-  const gchar *extension;
-
-  job->stream = zpj_skydrive_download_file_to_stream_finish (ZPJ_SKYDRIVE (source), res, &error);
-  if (error != NULL) {
-    pdf_load_job_complete_error (job, error);
-    return;
-  }
-
-  name = zpj_skydrive_entry_get_name (job->zpj_entry);
-  extension = gd_filename_get_extension_offset (name);
-
-  /* If it is not a PDF, we need to convert it afterwards.
-   * http://msdn.microsoft.com/en-us/library/live/hh826545#fileformats
-   */
-  if (g_strcmp0 (extension, ".pdf") != 0)
-    {
-      GFileIOStream *iostream;
-
-      job->download_file = g_file_new_tmp (NULL, &iostream, &error);
-      if (error != NULL) {
-        pdf_load_job_complete_error (job, error);
-        return;
-      }
-
-      /* We don't need the iostream. */
-      g_io_stream_close (G_IO_STREAM (iostream), NULL, NULL);
-    }
-  else
-    job->download_file = g_file_new_for_path (job->pdf_path);
-
-  g_file_replace_async (job->download_file, NULL, FALSE,
-                        G_FILE_CREATE_PRIVATE,
-                        G_PRIORITY_DEFAULT,
-                        job->cancellable, file_replace_ready_cb,
-                        job);
-}
-
-static void
-pdf_load_job_zpj_refresh_cache (PdfLoadJob *job)
-{
-  zpj_skydrive_download_file_to_stream_async (job->zpj_service,
-                                              ZPJ_SKYDRIVE_FILE (job->zpj_entry),
-                                              job->cancellable,
-                                              zpj_download_stream_ready,
-                                              job);
-}
-
-static void
 gdata_cache_query_info_ready_cb (GObject *source,
                                  GAsyncResult *res,
                                  gpointer user_data)
@@ -518,40 +452,6 @@ gdata_cache_query_info_ready_cb (GObject *source,
 }
 
 static void
-zpj_cache_query_info_ready_cb (GObject *source,
-                               GAsyncResult *res,
-                               gpointer user_data)
-{
-  PdfLoadJob *job = user_data;
-  GError *error = NULL;
-  GFileInfo *info;
-  guint64 cache_mtime;
-
-  info = g_file_query_info_finish (G_FILE (source), res, &error);
-
-  if (error != NULL) {
-    /* create/invalidate cache */
-    pdf_load_job_zpj_refresh_cache (job);
-    g_error_free (error);
-
-    return;
-  }
-
-  job->pdf_cache_mtime = cache_mtime =
-    g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
-  g_object_unref (info);
-
-  if (job->original_file_mtime != cache_mtime) {
-    pdf_load_job_zpj_refresh_cache (job);
-  } else {
-    job->from_old_cache = TRUE;
-
-    /* load the cached file */
-    pdf_load_job_from_pdf (job);
-  }
-}
-
-static void
 pdf_load_job_from_google_documents (PdfLoadJob *job)
 {
   gchar *tmp_name;
@@ -583,39 +483,6 @@ pdf_load_job_from_google_documents (PdfLoadJob *job)
 }
 
 static void
-pdf_load_job_from_skydrive (PdfLoadJob *job)
-{
-  gchar *tmp_name;
-  gchar *tmp_path, *pdf_path;
-  GDateTime *updated_time;
-  GFile *pdf_file;
-
-  updated_time = zpj_skydrive_entry_get_updated_time (job->zpj_entry);
-  job->original_file_mtime = (guint64) g_date_time_to_unix (updated_time);
-
-  tmp_name = g_strdup_printf ("gnome-documents-%u.pdf",
-                              g_str_hash (zpj_skydrive_entry_get_id (job->zpj_entry)));
-  tmp_path = g_build_filename (g_get_user_cache_dir (), "gnome-documents", NULL);
-  job->pdf_path = pdf_path =
-    g_build_filename (tmp_path, tmp_name, NULL);
-  g_mkdir_with_parents (tmp_path, 0700);
-
-  pdf_file = g_file_new_for_path (pdf_path);
-
-  g_file_query_info_async (pdf_file,
-                           G_FILE_ATTRIBUTE_TIME_MODIFIED,
-                           G_FILE_QUERY_INFO_NONE,
-                           G_PRIORITY_DEFAULT,
-                           job->cancellable,
-                           zpj_cache_query_info_ready_cb,
-                           job);
-
-  g_free (tmp_name);
-  g_free (tmp_path);
-  g_object_unref (pdf_file);
-}
-
-static void
 pdf_load_job_from_gdata_cache (PdfLoadJob *job)
 {
   gchar *tmp_name;
@@ -633,23 +500,6 @@ pdf_load_job_from_gdata_cache (PdfLoadJob *job)
 }
 
 static void
-pdf_load_job_from_zpj_cache (PdfLoadJob *job)
-{
-  gchar *tmp_name;
-  gchar *tmp_path;
-
-  tmp_name = g_strdup_printf ("gnome-documents-%u.pdf",
-                              g_str_hash (job->resource_id));
-  tmp_path = g_build_filename (g_get_user_cache_dir (), "gnome-documents", NULL);
-  job->pdf_path = g_build_filename (tmp_path, tmp_name, NULL);
-
-  pdf_load_job_from_pdf (job);
-
-  g_free (tmp_path);
-  g_free (tmp_name);
-}
-
-static void
 unoconv_cancelled_cb (GCancellable *cancellable,
                       gpointer user_data)
 {
@@ -1134,7 +984,6 @@ pdf_load_job_from_uri (PdfLoadJob *job)
 {
   GFile *file;
   const gchar *gdata_prefix = "google:drive:";
-  const gchar *zpj_prefix = "windows-live:skydrive:";
 
   if (g_str_has_prefix (job->uri, gdata_prefix)) {
     job->resource_id = g_strdup (job->uri + strlen (gdata_prefix));
@@ -1142,12 +991,6 @@ pdf_load_job_from_uri (PdfLoadJob *job)
     return;
   }
 
-  if (g_str_has_prefix (job->uri, zpj_prefix)) {
-    job->resource_id = g_strdup (job->uri + strlen (zpj_prefix));
-    pdf_load_job_from_zpj_cache (job);
-    return;
-  }
-
   file = g_file_new_for_uri (job->uri);
   if (!g_file_is_native (file))
     pdf_load_job_from_remote_file (job);
@@ -1162,8 +1005,6 @@ pdf_load_job_start (PdfLoadJob *job)
 {
   if (job->gdata_entry != NULL)
     pdf_load_job_from_google_documents (job);
-  else if (job->zpj_entry != NULL)
-    pdf_load_job_from_skydrive (job);
   else
     pdf_load_job_from_uri (job);
 }
@@ -1189,7 +1030,7 @@ gd_pdf_loader_load_uri_async (const gchar *uri,
   result = g_simple_async_result_new (NULL, callback, user_data,
                                       gd_pdf_loader_load_uri_async);
 
-  job = pdf_load_job_new (result, uri, NULL, NULL, passwd, cancellable);
+  job = pdf_load_job_new (result, uri, NULL, passwd, cancellable);
 
   pdf_load_job_start (job);
 
@@ -1230,7 +1071,7 @@ gd_pdf_loader_load_gdata_entry_async (GDataEntry *entry,
   result = g_simple_async_result_new (NULL, callback, user_data,
                                       gd_pdf_loader_load_gdata_entry_async);
 
-  job = pdf_load_job_new (result, NULL, entry, NULL, NULL, cancellable);
+  job = pdf_load_job_new (result, NULL, entry, NULL, cancellable);
   job->gdata_service = g_object_ref (service);
 
   pdf_load_job_start (job);
@@ -1257,45 +1098,3 @@ gd_pdf_loader_load_gdata_entry_finish (GAsyncResult *res,
   retval = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
   return retval;
 }
-
-
-void
-gd_pdf_loader_load_zpj_entry_async (ZpjSkydriveEntry *entry,
-                                    ZpjSkydrive *service,
-                                    GCancellable *cancellable,
-                                    GAsyncReadyCallback callback,
-                                    gpointer user_data)
-{
-  PdfLoadJob *job;
-  GSimpleAsyncResult *result;
-
-  result = g_simple_async_result_new (NULL, callback, user_data,
-                                      gd_pdf_loader_load_zpj_entry_async);
-
-  job = pdf_load_job_new (result, NULL, NULL, entry, NULL, cancellable);
-  job->zpj_service = g_object_ref (service);
-
-  pdf_load_job_start (job);
-
-  g_object_unref (result);
-}
-
-/**
- * gd_pdf_loader_load_zpj_entry_finish:
- * @res:
- * @error: (allow-none) (out):
- *
- * Returns: (transfer full):
- */
-EvDocumentModel *
-gd_pdf_loader_load_zpj_entry_finish (GAsyncResult *res,
-                                     GError **error)
-{
-  EvDocumentModel *retval;
-
-  if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
-    return NULL;
-
-  retval = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
-  return retval;
-}
diff --git a/src/lib/gd-pdf-loader.h b/src/lib/gd-pdf-loader.h
index 9e5ffb737eee..70ded295dad3 100644
--- a/src/lib/gd-pdf-loader.h
+++ b/src/lib/gd-pdf-loader.h
@@ -28,7 +28,6 @@
 
 #define GOA_API_IS_SUBJECT_TO_CHANGE
 #include <gdata/gdata.h>
-#include <zpj/zpj.h>
 
 G_BEGIN_DECLS
 
@@ -48,14 +47,6 @@ void gd_pdf_loader_load_gdata_entry_async (GDataEntry *entry,
 EvDocumentModel *gd_pdf_loader_load_gdata_entry_finish (GAsyncResult *res,
                                                         GError **error);
 
-void gd_pdf_loader_load_zpj_entry_async (ZpjSkydriveEntry *entry,
-                                         ZpjSkydrive *service,
-                                         GCancellable *cancellable,
-                                         GAsyncReadyCallback callback,
-                                         gpointer user_data);
-EvDocumentModel *gd_pdf_loader_load_zpj_entry_finish (GAsyncResult *res,
-                                                      GError **error);
-
 G_END_DECLS
 
 #endif /* __GD_PDF_LOADER_H__ */
-- 
2.9.4


From b80863a8d5666c9bd246a09bf770a8ea58367637 Mon Sep 17 00:00:00 2001
From: Debarshi Ray <debarshir@gnome.org>
Date: Thu, 20 Apr 2017 11:51:47 +0200
Subject: [PATCH 07/16] documents: Fix the retrieval of the cache's
 modification time

Gio.FILE_ATTRIBUTE_TIME_MODIFIED returns a value in seconds, not
microseconds.

Fallout from 4133adf06a0ff473e6234f8ab3eb88c47589e627

https://bugzilla.gnome.org/show_bug.cgi?id=774937
---
 src/documents.js | 2 --
 1 file changed, 2 deletions(-)

diff --git a/src/documents.js b/src/documents.js
index ca48e53db25d..4db7d07029b4 100644
--- a/src/documents.js
+++ b/src/documents.js
@@ -387,8 +387,6 @@ const DocCommon = new Lang.Class({
                 }
 
                 let cacheMtime = info.get_attribute_uint64(Gio.FILE_ATTRIBUTE_TIME_MODIFIED);
-                cacheMtime /= 1000000;
-
                 if (this.mtime <= cacheMtime) {
                     callback(true, null);
                     return;
-- 
2.9.4


From dd1343e7869882d632d75307b396de0c3fde4447 Mon Sep 17 00:00:00 2001
From: Debarshi Ray <debarshir@gnome.org>
Date: Tue, 18 Apr 2017 19:01:26 +0200
Subject: [PATCH 08/16] Optionally load the document when determining if it can
 be printed

https://bugzilla.gnome.org/show_bug.cgi?id=781533
---
 src/documents.js  | 99 ++++++++++++++++++++++++++++++++++++-------------------
 src/evinceview.js |  8 ++++-
 2 files changed, 72 insertions(+), 35 deletions(-)

diff --git a/src/documents.js b/src/documents.js
index 4db7d07029b4..fb0eee962548 100644
--- a/src/documents.js
+++ b/src/documents.js
@@ -36,6 +36,7 @@ const Zpj = imports.gi.Zpj;
 const _ = imports.gettext.gettext;
 
 const Lang = imports.lang;
+const Mainloop = imports.mainloop;
 const Signals = imports.signals;
 
 const Application = imports.application;
@@ -416,11 +417,37 @@ const DocCommon = new Lang.Class({
         log('Error: DocCommon implementations must override canTrash');
     },
 
-    canPrint: function(docModel) {
-        if (!docModel)
-            return false;
+    canPrint: function(docModel, cancellable, callback) {
+        if (docModel) {
+            Mainloop.idle_add(Lang.bind(this,
+                function() {
+                    this._canPrint(docModel, callback);
+                    return GLib.SOURCE_REMOVE;
+                }));
+
+            return;
+        }
+
+        this.load(null, cancellable, Lang.bind(this,
+            function(doc, docModel, error) {
+                if (error) {
+                    callback(this, false);
+                    return;
+                }
+
+                this._canPrint(docModel, callback);
+            }));
+    },
+
+    _canPrint: function(docModel, callback) {
+        if (!docModel) {
+            callback(this, false);
+            return;
+        }
 
-        return EvView.PrintOperation.exists_for_document(docModel.get_document());
+        let evDoc = docModel.get_document();
+        let supported = EvView.PrintOperation.exists_for_document(evDoc);
+        callback(this, supported);
     },
 
     trash: function() {
@@ -694,41 +721,45 @@ const DocCommon = new Lang.Class({
                     return;
                 }
 
-                if (!this.canPrint(docModel))
-                    return;
+                this.canPrint(docModel, null, Lang.bind(this,
+                    function(doc, supported) {
+                        if (!supported)
+                            return;
 
-                let printOp = EvView.PrintOperation.new(docModel.get_document());
+                        let printOp = EvView.PrintOperation.new(docModel.get_document());
 
-                printOp.connect('begin-print', Lang.bind(this,
-                    function() {
-                        Application.selectionController.setSelectionMode(false);
-                    }));
 
-                printOp.connect('done', Lang.bind(this,
-                    function(op, res) {
-                        if (res == Gtk.PrintOperationResult.ERROR) {
-                            try {
-                                printOp.get_error();
-                            } catch (e) {
-                                let errorDialog = new Gtk.MessageDialog ({ transient_for: toplevel,
-                                                                           modal: true,
-                                                                           destroy_with_parent: true,
-                                                                           buttons: Gtk.ButtonsType.OK,
-                                                                           message_type: Gtk.MessageType.ERROR,
-                                                                           text: _("Failed to print document"),
-                                                                           secondary_text: e.message });
-                                errorDialog.connect ('response', Lang.bind(this,
-                                    function() {
-                                        errorDialog.destroy();
-                                    }));
-                                errorDialog.show();
-                            }
-                        }
-                    }));
+                        printOp.connect('begin-print', Lang.bind(this,
+                            function() {
+                                Application.selectionController.setSelectionMode(false);
+                            }));
 
-                let printNotification = new Notifications.PrintNotification(printOp, doc);
+                        printOp.connect('done', Lang.bind(this,
+                            function(op, res) {
+                                if (res == Gtk.PrintOperationResult.ERROR) {
+                                    try {
+                                        printOp.get_error();
+                                    } catch (e) {
+                                        let errorDialog = new Gtk.MessageDialog ({ transient_for: toplevel,
+                                                                                   modal: true,
+                                                                                   destroy_with_parent: true,
+                                                                                   buttons: Gtk.ButtonsType.OK,
+                                                                                   message_type: Gtk.MessageType.ERROR,
+                                                                                   text: _("Failed to print document"),
+                                                                                   secondary_text: e.message });
+                                        errorDialog.connect ('response', Lang.bind(this,
+                                            function() {
+                                                errorDialog.destroy();
+                                            }));
+                                        errorDialog.show();
+                                    }
+                                }
+                            }));
+
+                        let printNotification = new Notifications.PrintNotification(printOp, doc);
 
-                printOp.run(toplevel);
+                        printOp.run(toplevel);
+                    }));
             }));
     },
 
diff --git a/src/evinceview.js b/src/evinceview.js
index d4ea883e76b4..3e0070bb785c 100644
--- a/src/evinceview.js
+++ b/src/evinceview.js
@@ -264,7 +264,13 @@ const EvinceView = new Lang.Class({
 
         this.getAction('copy').enabled = false;
         this.getAction('edit-current').enabled = doc.canEdit();
-        this.getAction('print-current').enabled = doc.canPrint(docModel);
+
+        this.getAction('print-current').enabled = false;
+        doc.canPrint(docModel, null, Lang.bind(this,
+            function(doc, supported) {
+                this.getAction('print-current').enabled = supported;
+            }));
+
         let presentCurrent = this.getAction('present-current');
         if (presentCurrent)
             presentCurrent.enabled = true;
-- 
2.9.4


From c37a9a62610ba301f09d2e53a4004dcbf12dadec Mon Sep 17 00:00:00 2001
From: Debarshi Ray <debarshir@gnome.org>
Date: Thu, 20 Apr 2017 14:40:40 +0200
Subject: [PATCH 09/16] documents: Check whether it is a collection in canPrint

https://bugzilla.gnome.org/show_bug.cgi?id=781533
---
 src/documents.js | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/src/documents.js b/src/documents.js
index fb0eee962548..3bed18c36a49 100644
--- a/src/documents.js
+++ b/src/documents.js
@@ -418,6 +418,16 @@ const DocCommon = new Lang.Class({
     },
 
     canPrint: function(docModel, cancellable, callback) {
+        if (this.collection) {
+            Mainloop.idle_add(Lang.bind(this,
+                function() {
+                    callback(this, false);
+                    return GLib.SOURCE_REMOVE;
+                }));
+
+            return;
+        }
+
         if (docModel) {
             Mainloop.idle_add(Lang.bind(this,
                 function() {
-- 
2.9.4


From 7d63e3fc760aef6ec56868a16e930af8977f0973 Mon Sep 17 00:00:00 2001
From: Debarshi Ray <debarshir@gnome.org>
Date: Thu, 20 Apr 2017 14:59:12 +0200
Subject: [PATCH 10/16] selections: Enable printing only for documents handled
 by EvView

Now that we don't convert everything to PDFs, we might not be able to
print everything that's not a collection. eg., we can't print EPUBs,
ODFs and OOXMLs. Therefore it is not enough to only check for
collections.

Instead, let's use canPrint which encapsulates all the conditions that
need to be met for printing.

https://bugzilla.gnome.org/show_bug.cgi?id=781533
---
 src/selections.js | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/src/selections.js b/src/selections.js
index b864c8f8a634..ecfd3ec694c7 100644
--- a/src/selections.js
+++ b/src/selections.js
@@ -946,7 +946,7 @@ const SelectionToolbar = new Lang.Class({
         let hasSelection = (selection.length > 0);
 
         let showTrash = hasSelection;
-        let showPrint = hasSelection;
+        let showPrint = false;
         let showProperties = hasSelection;
         let showOpen = hasSelection;
         let showShare = hasSelection;
@@ -967,16 +967,21 @@ const SelectionToolbar = new Lang.Class({
                     showShare = false;
 
                 showTrash &= doc.canTrash();
-                showPrint &= !doc.collection;
             }));
 
         showOpen = (apps.length > 0);
 
-        if (selection.length > 1) {
-            showPrint = false;
-            showProperties = false;
+        if (selection.length == 1) {
+            let doc = Application.documentManager.getItemById(selection[0]);
+            doc.canPrint(null, null, Lang.bind(this,
+                function(doc, supported) {
+                    this._toolbarPrint.set_sensitive(supported);
+                }));
         }
 
+        if (selection.length > 1)
+            showProperties = false;
+
         let openLabel = null;
         if (apps.length == 1) {
             // Translators: this is the Open action in a context menu
-- 
2.9.4


From aa507bc55aef99815308fe7865f016d40ed6f76d Mon Sep 17 00:00:00 2001
From: Debarshi Ray <debarshir@gnome.org>
Date: Mon, 27 Mar 2017 16:08:57 +0200
Subject: [PATCH 11/16] documents: Thumbnail SkydriveDocuments once they are
 loaded
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Historically, unlike GoogleDocuments, we were unable to fetch
server-side thumbnails for SkydriveDocuments due to limitations of
OneDrive's REST API. Newer versions of the REST API do support it [1],
but it is not implemented in libzapojit.

Some ways to work around these limitations:
  - Create the thumbnail from the EvDocument when it is loaded for
    previewing
  - Use a cached copy of the document as source for the thumbnail

Even if we can fetch thumbnails from the server in future, these would
still be nice optimizations to have — reduces network consumption and
offers a cheap way to jump ahead in the thumbnailing queue.

[1] https://dev.onedrive.com/items/thumbnails.htm
---
 src/documents.js | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 109 insertions(+), 2 deletions(-)

diff --git a/src/documents.js b/src/documents.js
index 3bed18c36a49..27518b2251d1 100644
--- a/src/documents.js
+++ b/src/documents.js
@@ -1150,7 +1150,7 @@ const SkydriveDocument = new Lang.Class({
     Extends: DocCommon,
 
     _init: function(cursor) {
-        this._failedThumbnailing = true;
+        this._failedThumbnailing = false;
 
         this.parent(cursor);
 
@@ -1174,6 +1174,54 @@ const SkydriveDocument = new Lang.Class({
         this.uriToLoad = localFile.get_uri();
     },
 
+    _createThumbnailFromEvDocument: function(evDoc, cancellable, callback) {
+        let thumbnailPath = GnomeDesktop.desktop_thumbnail_path_for_uri (this.uri,
+                                                                         GnomeDesktop.DesktopThumbnailSize.LARGE);
+        let thumbnailFile = Gio.File.new_for_path(thumbnailPath);
+
+        let thumbnailDir = GLib.path_get_dirname(thumbnailPath);
+        GLib.mkdir_with_parents(thumbnailDir, 448);
+
+        thumbnailFile.replace_async(null,
+                                    false,
+                                    Gio.FileCreateFlags.PRIVATE,
+                                    GLib.PRIORITY_DEFAULT,
+                                    cancellable,
+                                    Lang.bind(this,
+            function(source, res) {
+                let outputStream;
+
+                try {
+                    outputStream = thumbnailFile.replace_finish(res);
+                } catch (e) {
+                    callback(e);
+                    return;
+                }
+
+                let [width, height] = evDoc.get_page_size(0);
+                let maxDimension = Math.max(width, height);
+                let scale = Application.application.getScaleFactor();
+                let size = 128 * scale;
+                let zoom = size / maxDimension;
+
+                let page = evDoc.get_page(0);
+
+                let rc = EvDocument.RenderContext.new(page, 0, zoom);
+                let pixbuf = evDoc.get_thumbnail(rc);
+                pixbuf.save_to_streamv_async(outputStream, "png", [], [], cancellable, Lang.bind(this,
+                    function(source, res) {
+                        try {
+                            GdkPixbuf.Pixbuf.save_to_stream_finish(res);
+                        } catch (e) {
+                            callback(e);
+                            return;
+                        }
+
+                        callback(null);
+                    }));
+            }));
+    },
+
     _createZpjEntry: function(cancellable, callback) {
         let source = Application.sourceManager.getItemById(this.resourceUrn);
 
@@ -1276,7 +1324,16 @@ const SkydriveDocument = new Lang.Class({
                                             return;
                                         }
 
-                                        this.loadLocal(passwd, cancellable, callback);
+                                        this.loadLocal(passwd, cancellable, Lang.bind(this,
+                                            function(doc, docModel, error) {
+                                                if (error) {
+                                                    callback(this, null, error);
+                                                    return;
+                                                }
+
+                                                callback(this, docModel, null);
+                                                this._postLoad(docModel);
+                                            }));
                                     }));
                             } else {
                                 callback(this, null, error);
@@ -1286,6 +1343,56 @@ const SkydriveDocument = new Lang.Class({
                         }
 
                         callback(this, docModel, null);
+                        this._postLoad(docModel);
+                    }));
+            }));
+    },
+
+    _postLoad: function(docModel) {
+        if (this._thumbPath)
+            return;
+
+        if (!docModel)
+            return;
+
+        this._createThumbnailFromEvDocument(docModel.document, null, Lang.bind(this,
+            function(error) {
+                if (error) {
+                    logError(error, 'Unable to create thumbnail from EvDocument');
+                    return;
+                }
+
+                this._failedThumbnailing = false;
+                this.refreshIcon();
+            }));
+    },
+
+    createThumbnail: function(callback) {
+        // try loading from the most recent cache, if any
+        this.loadLocal(null, null, Lang.bind(this,
+            function(doc, docModel, error) {
+                if (error) {
+                    if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND) &&
+                        !error.matches(EvDocument.DocumentError, EvDocument.DocumentError.ENCRYPTED)) {
+                        logError(error, 'Unable to load document from the cache');
+                        callback(false);
+                        return;
+                    }
+                }
+
+                if (!docModel) {
+                    callback(false);
+                    return;
+                }
+
+                this._createThumbnailFromEvDocument(docModel.document, null, Lang.bind(this,
+                    function(error) {
+                        if (error) {
+                            logError(error, 'Unable to create thumbnail from EvDocument');
+                            return;
+                        }
+
+                        callback(true);
                     }));
             }));
     },
-- 
2.9.4


From 3bd516524432f9decb0a15ab209a9957b7ae7488 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Caol=C3=A1n=20McNamara?= <caolanm@redhat.com>
Date: Mon, 8 May 2017 16:44:32 +0100
Subject: [PATCH 12/16] lokview: Fix crash on repeated open of presentations

Ensure prompt destruction of the LOKDocView widget when the view is
destroyed. This gives LOK a chance to shutdown at the right time.

https://bugzilla.gnome.org/show_bug.cgi?id=782508
---
 src/lokview.js | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/src/lokview.js b/src/lokview.js
index 988db963d5d9..238832ed4502 100644
--- a/src/lokview.js
+++ b/src/lokview.js
@@ -137,11 +137,19 @@ const LOKView = new Lang.Class({
             this._lokview.connect('text-selection', Lang.bind(this, this._onTextSelection));
             this._lokview.connect('notify::can-zoom-in', Lang.bind(this, this._onCanZoomInChanged));
             this._lokview.connect('notify::can-zoom-out', Lang.bind(this, this._onCanZoomOutChanged));
+            this.connect('destroy', Lang.bind(this, this._destroyView));
         }
 
         return sw;
     },
 
+    _destroyView: function() {
+	if (this._lokview) {
+	    this._lokview.destroy();
+	    this._lokview = null;
+	}
+    },
+
     onLoadFinished: function(manager, doc) {
         this.parent(manager, doc);
 
@@ -237,10 +245,10 @@ const LOKView = new Lang.Class({
     },
 
     get page() {
-        return this._lokview.get_part();
+        return this._lokview ? this._lokview.get_part() : 0;
     },
 
     get numPages() {
-        return this._lokview.get_parts();
+        return this._lokview ? this._lokview.get_parts() : 0;
     }
 });
-- 
2.9.4


From 238a4d4c95b78eff9d1a764f184936ddc58db73d Mon Sep 17 00:00:00 2001
From: Debarshi Ray <debarshir@gnome.org>
Date: Thu, 8 Jun 2017 16:10:57 +0200
Subject: [PATCH 13/16] application: Avoid CRITICALs if a primary instance is
 already present

The dbus_unregister method can be invoked more than once. Trying to
unexport an already unexported GDBusInterfaceSkeleton led to:
  GLib-GIO-CRITICAL **:
    g_dbus_interface_skeleton_unexport_from_connection: assertion
    'interface_->priv->connections != NULL' failed

https://bugzilla.gnome.org/show_bug.cgi?id=783548
---
 src/application.js | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/application.js b/src/application.js
index a82395d6afd2..40a5dd0e5114 100644
--- a/src/application.js
+++ b/src/application.js
@@ -521,7 +521,10 @@ const Application = new Lang.Class({
     },
 
     vfunc_dbus_unregister: function(connection, path) {
-        this._searchProvider.unexport(connection);
+        if (this._searchProvider != null) {
+            this._searchProvider.unexport(connection);
+            this._searchProvider = null;
+        }
 
         this.parent(connection, path);
     },
-- 
2.9.4


From ca10b8d02486630db2f3de746ee4b885490cbaab Mon Sep 17 00:00:00 2001
From: Debarshi Ray <debarshir@gnome.org>
Date: Thu, 8 Jun 2017 16:12:15 +0200
Subject: [PATCH 14/16] application: Instantiate ShellSearchProvider only when
 registering

Now that the --version flag was implemented using the
handle_local_options virtual method, it is possible that the
Application may exit without ever touching D-Bus. So it is a tad
wasteful to instantiate the ShellSearchProvider when it is never going
to be used. Since we are unreffing it in dbus_register, we might as
well create it in dbus_register.

https://bugzilla.gnome.org/show_bug.cgi?id=783548
---
 src/application.js | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/src/application.js b/src/application.js
index 40a5dd0e5114..2630e1971826 100644
--- a/src/application.js
+++ b/src/application.js
@@ -100,6 +100,7 @@ const Application = new Lang.Class({
         this.minersRunning = [];
         this._activationTimestamp = Gdk.CURRENT_TIME;
         this._extractPriority = null;
+        this._searchProvider = null;
 
         this.isBooks = isBooks;
 
@@ -120,10 +121,6 @@ const Application = new Lang.Class({
 
         this.add_main_option('version', 'v'.charCodeAt(0), GLib.OptionFlags.NONE, GLib.OptionArg.NONE,
                              _("Show the version of the program"), null);
-
-        this._searchProvider = new ShellSearchProvider.ShellSearchProvider();
-        this._searchProvider.connect('activate-result', Lang.bind(this, this._onActivateResult));
-        this._searchProvider.connect('launch-search', Lang.bind(this, this._onLaunchSearch));
     },
 
     _initGettingStarted: function() {
@@ -516,6 +513,10 @@ const Application = new Lang.Class({
     vfunc_dbus_register: function(connection, path) {
         this.parent(connection, path);
 
+        this._searchProvider = new ShellSearchProvider.ShellSearchProvider();
+        this._searchProvider.connect('activate-result', Lang.bind(this, this._onActivateResult));
+        this._searchProvider.connect('launch-search', Lang.bind(this, this._onLaunchSearch));
+
         this._searchProvider.export(connection);
         return true;
     },
-- 
2.9.4


From af6130bfe229521869f33a125dd4d354f4327ff4 Mon Sep 17 00:00:00 2001
From: Debarshi Ray <debarshir@gnome.org>
Date: Thu, 8 Jun 2017 16:12:34 +0200
Subject: [PATCH 15/16] application: Assert that the ShellSearchProvider's
 lifetime is sane

The ShellSearchProvider should be instantiated in the dbus_register
virtual method, which is expected to be called only once.

https://bugzilla.gnome.org/show_bug.cgi?id=783548
---
 src/application.js | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/application.js b/src/application.js
index 2630e1971826..1e439815f0b4 100644
--- a/src/application.js
+++ b/src/application.js
@@ -513,6 +513,9 @@ const Application = new Lang.Class({
     vfunc_dbus_register: function(connection, path) {
         this.parent(connection, path);
 
+        if (this._searchProvider != null)
+            throw(new Error('ShellSearchProvider already instantiated - dbus_register called twice?'));
+
         this._searchProvider = new ShellSearchProvider.ShellSearchProvider();
         this._searchProvider.connect('activate-result', Lang.bind(this, this._onActivateResult));
         this._searchProvider.connect('launch-search', Lang.bind(this, this._onLaunchSearch));
-- 
2.9.4


From 4d3476a700ecbb5066269f9fb15fc201d134859e Mon Sep 17 00:00:00 2001
From: Debarshi Ray <debarshir@gnome.org>
Date: Thu, 8 Jun 2017 16:58:08 +0200
Subject: [PATCH 16/16] application: Don't unexport a skeleton that was never
 exported

Otherwise it will again lead to:
  GLib-GIO-CRITICAL **:
    g_dbus_interface_skeleton_unexport_from_connection: assertion
    'interface_->priv->connections != NULL' failed

https://bugzilla.gnome.org/show_bug.cgi?id=783548
---
 src/application.js | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/src/application.js b/src/application.js
index 1e439815f0b4..f9e8b6ae9863 100644
--- a/src/application.js
+++ b/src/application.js
@@ -520,7 +520,13 @@ const Application = new Lang.Class({
         this._searchProvider.connect('activate-result', Lang.bind(this, this._onActivateResult));
         this._searchProvider.connect('launch-search', Lang.bind(this, this._onLaunchSearch));
 
-        this._searchProvider.export(connection);
+        try {
+            this._searchProvider.export(connection);
+        } catch(e) {
+            this._searchProvider = null;
+            throw(e);
+        }
+
         return true;
     },
 
-- 
2.9.4