From 9236afae0034d0b337deff6c08be64fd7f3cc8d0 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Mon, 24 Jun 2013 14:32:32 -0400 Subject: [PATCH] Bug 870100 (part 1) - regenerate stale page thumbnails on demand, and send notification when done. r=adw --- toolkit/components/thumbnails/PageThumbs.jsm | 30 ++++++++++- .../components/thumbnails/PageThumbsWorker.js | 14 ++++++ .../components/thumbnails/test/Makefile.in | 1 + .../test/browser_thumbnails_update.js | 50 +++++++++++++++++++ 4 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 toolkit/components/thumbnails/test/browser_thumbnails_update.js diff --git a/toolkit/components/thumbnails/PageThumbs.jsm b/toolkit/components/thumbnails/PageThumbs.jsm index ee97f8ea714e..3c8ec8f2149e 100644 --- a/toolkit/components/thumbnails/PageThumbs.jsm +++ b/toolkit/components/thumbnails/PageThumbs.jsm @@ -17,6 +17,11 @@ const LATEST_STORAGE_VERSION = 3; const EXPIRATION_MIN_CHUNK_SIZE = 50; const EXPIRATION_INTERVAL_SECS = 3600; +// If a request for a thumbnail comes in and we find one that is "stale" +// (or don't find one at all) we automatically queue a request to generate a +// new one. +const MAX_THUMBNAIL_AGE_SECS = 172800; // 2 days == 60*60*24*2 == 172800 secs. + /** * Name of the directory in the profile that contains the thumbnails. */ @@ -180,6 +185,26 @@ this.PageThumbs = { "?url=" + encodeURIComponent(aUrl); }, + /** + * Checks if an existing thumbnail for the specified URL is either missing + * or stale, and if so, queues a background request to capture it. That + * capture process will send a notification via the observer service on + * capture, so consumers should watch for such observations if they want to + * be notified of an updated thumbnail. + */ + captureIfStale: function PageThumbs_captureIfStale(aUrl) { + let filePath = PageThumbsStorage.getFilePathForURL(aUrl); + PageThumbsWorker.post("isFileRecent", [filePath, MAX_THUMBNAIL_AGE_SECS] + ).then(result => { + if (!result.ok) { + // Sadly there is currently a circular dependency between this module + // and BackgroundPageThumbs, so do the import locally. + let BPT = Cu.import("resource://gre/modules/BackgroundPageThumbs.jsm", {}).BackgroundPageThumbs; + BPT.capture(aUrl); + } + }); + }, + /** * Captures a thumbnail for the given window. * @param aWindow The DOM window to capture a thumbnail from. @@ -310,6 +335,7 @@ this.PageThumbs = { Services.telemetry.getHistogramById("FX_THUMBNAILS_STORE_TIME_MS") .add(new Date() - telemetryStoreTime); + Services.obs.notifyObservers(null, "page-thumbnail:create", aFinalURL); // We've been redirected. Create a copy of the current thumbnail for // the redirect source. We need to do this because: // @@ -321,8 +347,10 @@ this.PageThumbs = { // Because of bug 559175 this information can get lost when using // Sync and therefore also redirect sources appear on the newtab // page. We also want thumbnails for those. - if (aFinalURL != aOriginalURL) + if (aFinalURL != aOriginalURL) { yield PageThumbsStorage.copy(aFinalURL, aOriginalURL); + Services.obs.notifyObservers(null, "page-thumbnail:create", aOriginalURL); + } }); }, diff --git a/toolkit/components/thumbnails/PageThumbsWorker.js b/toolkit/components/thumbnails/PageThumbsWorker.js index 9adcc2db65ee..a6ea6320e7f4 100644 --- a/toolkit/components/thumbnails/PageThumbsWorker.js +++ b/toolkit/components/thumbnails/PageThumbsWorker.js @@ -53,6 +53,20 @@ self.onmessage = function onmessage(msg) { let Agent = { + // Checks if the specified file exists and has an age less than as + // specifed (in seconds). + isFileRecent: function Agent_isFileRecent(path, maxAge) { + try { + let stat = OS.File.stat(path); + let maxDate = new Date(); + maxDate.setSeconds(maxDate.getSeconds() - maxAge); + return stat.lastModificationDate > maxDate; + } catch (ex if ex instanceof OS.File.Error) { + // file doesn't exist (or can't be stat'd) - must be stale. + return false; + } + }, + remove: function Agent_removeFile(path) { try { OS.File.remove(path); diff --git a/toolkit/components/thumbnails/test/Makefile.in b/toolkit/components/thumbnails/test/Makefile.in index 451fdc51fc65..2e979d428ad9 100644 --- a/toolkit/components/thumbnails/test/Makefile.in +++ b/toolkit/components/thumbnails/test/Makefile.in @@ -20,6 +20,7 @@ MOCHITEST_BROWSER_FILES := \ browser_thumbnails_storage_migrate3.js \ browser_thumbnails_bug726727.js \ browser_thumbnails_bug727765.js \ + browser_thumbnails_update.js \ head.js \ background_red.html \ background_red_scroll.html \ diff --git a/toolkit/components/thumbnails/test/browser_thumbnails_update.js b/toolkit/components/thumbnails/test/browser_thumbnails_update.js new file mode 100644 index 000000000000..d7a4bdba34e3 --- /dev/null +++ b/toolkit/components/thumbnails/test/browser_thumbnails_update.js @@ -0,0 +1,50 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * These tests check the auto-update facility of the thumbnail service. + */ + +let numNotifications = 0; +const URL = "data:text/html;charset=utf-8,"; + +function observe(subject, topic, data) { + is(topic, "page-thumbnail:create", "got expected topic"); + is(data, URL, "data is our test URL"); + if (++numNotifications == 2) { + // This is the final notification and signals test success... + Services.obs.removeObserver(observe, "page-thumbnail:create"); + next(); + } +} + +function runTests() { + Services.obs.addObserver(observe, "page-thumbnail:create", false); + // Create a tab with a red background. + yield addTab(URL); + let browser = gBrowser.selectedBrowser; + + // Capture the screenshot. + PageThumbs.captureAndStore(browser, function () { + // We've got a capture so should have seen the observer. + is(numNotifications, 1, "got notification of item being created."); + // The capture is now "fresh" - so requesting the URL should not cause + // a new capture. + PageThumbs.captureIfStale(URL); + is(numNotifications, 1, "still only 1 notification of item being created."); + + // Now we will go behind the back of the thumbnail service and change the + // mtime of the file to be in the past. + let fname = PageThumbsStorage.getFilePathForURL(URL); + let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); + file.initWithPath(fname); + ok(file.exists(), fname + " doesn't exist"); + // Set it as very stale... + file.lastModifiedTime = Date.now() - 1000000000; + // Ask for it to be updated. + PageThumbs.captureIfStale(URL); + // But it's async, so wait - our observer above will call next() when + // the notification comes. + }); + yield undefined; // wait for callbacks to call 'next'... +}