Bug 1234331 - Push article into readercache when bookmarking readerview page r?margaret
MozReview-Commit-ID: D7Yy45xkFd8
--- a/mobile/android/base/java/org/mozilla/gecko/Tab.java
+++ b/mobile/android/base/java/org/mozilla/gecko/Tab.java
@@ -20,16 +20,17 @@ import org.mozilla.gecko.db.BrowserDB;
import org.mozilla.gecko.db.URLMetadata;
import org.mozilla.gecko.favicons.Favicons;
import org.mozilla.gecko.favicons.LoadFaviconTask;
import org.mozilla.gecko.favicons.OnFaviconLoadedListener;
import org.mozilla.gecko.favicons.RemoteFavicon;
import org.mozilla.gecko.gfx.BitmapUtils;
import org.mozilla.gecko.gfx.Layer;
import org.mozilla.gecko.reader.ReaderModeUtils;
+import org.mozilla.gecko.reader.ReadingListHelper;
import org.mozilla.gecko.toolbar.BrowserToolbar.TabEditingState;
import org.mozilla.gecko.util.ThreadUtils;
import android.content.ContentResolver;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
@@ -565,16 +566,20 @@ public class Tab {
ThreadUtils.postToBackgroundThread(new Runnable() {
@Override
public void run() {
mDB.addBookmark(getContentResolver(), mTitle, pageUrl);
Tabs.getInstance().notifyListeners(Tab.this, Tabs.TabEvents.BOOKMARK_ADDED);
}
});
+
+ if (AboutPages.isAboutReader(url)) {
+ ReadingListHelper.cacheReaderItem(pageUrl, mAppContext);
+ }
}
public void removeBookmark() {
final String url = getURL();
if (url == null) {
return;
}
--- a/mobile/android/base/java/org/mozilla/gecko/reader/ReadingListHelper.java
+++ b/mobile/android/base/java/org/mozilla/gecko/reader/ReadingListHelper.java
@@ -1,16 +1,18 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.reader;
import org.json.JSONException;
import org.json.JSONObject;
+
+import org.mozilla.gecko.AboutPages;
import org.mozilla.gecko.EventDispatcher;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoEvent;
import org.mozilla.gecko.GeckoProfile;
import org.mozilla.gecko.annotation.RobocopTarget;
import org.mozilla.gecko.db.BrowserContract.ReadingListItems;
import org.mozilla.gecko.db.BrowserDB;
import org.mozilla.gecko.db.ReadingListAccessor;
@@ -52,18 +54,17 @@ public final class ReadingListHelper imp
volatile boolean fetchInBackground = true;
public ReadingListHelper(Context context, GeckoProfile profile, OnReadingListEventListener listener) {
this.context = context;
this.db = profile.getDB();
this.readingListAccessor = db.getReadingListAccessor();
EventDispatcher.getInstance().registerGeckoThreadListener((NativeEventListener) this,
- "Reader:AddToList", "Reader:UpdateList", "Reader:FaviconRequest");
-
+ "Reader:AddToList", "Reader:UpdateList", "Reader:FaviconRequest", "Reader:AddedToCache");
contentObserver = new ContentObserver(null) {
@Override
public void onChange(boolean selfChange) {
if (fetchInBackground) {
fetchContent();
}
}
@@ -71,17 +72,17 @@ public final class ReadingListHelper imp
this.readingListAccessor.registerContentObserver(context, contentObserver);
onReadingListEventListener = listener;
}
public void uninit() {
EventDispatcher.getInstance().unregisterGeckoThreadListener((NativeEventListener) this,
- "Reader:AddToList", "Reader:UpdateList", "Reader:FaviconRequest");
+ "Reader:AddToList", "Reader:UpdateList", "Reader:FaviconRequest", "Reader:AddedToCache");
context.getContentResolver().unregisterContentObserver(contentObserver);
}
@Override
public void handleMessage(final String event, final NativeJSObject message,
final EventCallback callback) {
switch(event) {
@@ -93,16 +94,22 @@ public final class ReadingListHelper imp
case "Reader:UpdateList": {
handleUpdateList(message);
break;
}
case "Reader:FaviconRequest": {
handleReaderModeFaviconRequest(callback, message.getString("url"));
break;
}
+ case "Reader:AddedToCache": {
+ // AddedToCache is a one way message: callback will be null, and we therefore shouldn't
+ // attempt to handle it.
+ handleAddedToCache(message.getString("url"), message.getString("path"), message.getInt("size"));
+ break;
+ }
}
}
/**
* A page can be added to the ReadingList by long-tap of the page-action
* icon, or by tapping the readinglist-add icon in the ReaderMode banner.
*
* This method will only add new items, not update existing items.
@@ -222,24 +229,30 @@ public final class ReadingListHelper imp
Log.w(LOGTAG, "Error building JSON favicon arguments.", e);
}
}
callback.sendSuccess(args.toString());
}
}).execute();
}
+ private void handleAddedToCache(final String url, final String path, final int size) {
+ final ReaderCacheHelper rch = GeckoProfile.get(context).getReaderCacheHelper();
+
+ rch.put(url, path, size);
+ }
+
/**
* Handle various reading list events (and display appropriate toasts).
*/
private void handleEvent(final ReadingListEvent event, final String url) {
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
- switch(event) {
+ switch (event) {
case ADDED:
onReadingListEventListener.onAddedToReadingList(url);
break;
case REMOVED:
onReadingListEventListener.onRemovedFromReadingList(url);
break;
case ALREADY_EXISTS:
onReadingListEventListener.onAlreadyInReadingList(url);
@@ -271,16 +284,29 @@ public final class ReadingListHelper imp
}
} finally {
c.close();
}
}
});
}
+ public static void cacheReaderItem(final String url, Context context) {
+ if (AboutPages.isAboutReader(url)) {
+ throw new IllegalArgumentException("Page url must be original (not about:reader) url");
+ }
+
+ ReaderCacheHelper rch = GeckoProfile.get(context).getReaderCacheHelper();
+
+ if (!rch.isURLCached(url)) {
+ GeckoAppShell.sendEventToGecko(
+ GeckoEvent.createBroadcastEvent("Reader:AddToCache", url));
+ }
+ }
+
@RobocopTarget
/**
* Test code will want to disable background fetches to avoid upsetting
* the test harness. Call this by accessing the instance from BrowserApp.
*/
public void disableBackgroundFetches() {
fetchInBackground = false;
}
--- a/mobile/android/chrome/content/Reader.js
+++ b/mobile/android/chrome/content/Reader.js
@@ -75,16 +75,25 @@ var Reader = {
this._fetchContent(data.url, data.id);
break;
}
case "Reader:Removed": {
ReaderMode.removeArticleFromCache(aData).catch(e => Cu.reportError("Error removing article from cache: " + e));
break;
}
+
+ case "Reader:AddToCache": {
+ // If the article is coming from reader mode, we must have fetched it already.
+ this._getArticle(aData).then((article) => {
+ console.log("trying");
+ ReaderMode.storeArticleInCache(article).catch(e => Cu.reportError("Error storing article in cache: " + e));
+ });
+ break;
+ }
}
},
receiveMessage: function(message) {
switch (message.name) {
case "Reader:ArticleGet":
this._getArticle(message.data.url).then((article) => {
// Make sure the target browser is still alive before trying to send data back.
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -147,17 +147,17 @@ var lazilyLoadedObserverScripts = [
["MemoryObserver", ["memory-pressure", "Memory:Dump"], "chrome://browser/content/MemoryObserver.js"],
["ConsoleAPI", ["console-api-log-event"], "chrome://browser/content/ConsoleAPI.js"],
["FindHelper", ["FindInPage:Opened", "FindInPage:Closed", "Tab:Selected"], "chrome://browser/content/FindHelper.js"],
["PermissionsHelper", ["Permissions:Check", "Permissions:Get", "Permissions:Clear"], "chrome://browser/content/PermissionsHelper.js"],
["FeedHandler", ["Feeds:Subscribe"], "chrome://browser/content/FeedHandler.js"],
["Feedback", ["Feedback:Show"], "chrome://browser/content/Feedback.js"],
["SelectionHandler", ["TextSelection:Get"], "chrome://browser/content/SelectionHandler.js"],
["EmbedRT", ["GeckoView:ImportScript"], "chrome://browser/content/EmbedRT.js"],
- ["Reader", ["Reader:FetchContent", "Reader:Removed"], "chrome://browser/content/Reader.js"],
+ ["Reader", ["Reader:FetchContent", "Reader:AddToCache", "Reader:Removed"], "chrome://browser/content/Reader.js"],
["PrintHelper", ["Print:PDF"], "chrome://browser/content/PrintHelper.js"],
];
if (AppConstants.NIGHTLY_BUILD) {
lazilyLoadedObserverScripts.push(
["ActionBarHandler", ["TextSelection:Get", "TextSelection:Action", "TextSelection:End"],
"chrome://browser/content/ActionBarHandler.js"]
);
}
@@ -183,16 +183,17 @@ lazilyLoadedObserverScripts.forEach(func
notifications.forEach((notification) => {
Services.obs.addObserver(observer, notification, false);
});
});
// Lazily-loaded browser scripts that use message listeners.
[
["Reader", [
+ ["Reader:AddToCache", false],
["Reader:ArticleGet", false],
["Reader:DropdownClosed", true], // 'true' allows us to survive mid-air cycle-collection.
["Reader:DropdownOpened", false],
["Reader:FaviconRequest", false],
["Reader:ToolbarHidden", false],
["Reader:SystemUIVisibility", false],
["Reader:UpdateReaderButton", false],
["Reader:SetIntPref", false],
--- a/toolkit/components/reader/ReaderMode.jsm
+++ b/toolkit/components/reader/ReaderMode.jsm
@@ -19,16 +19,17 @@ const PARSE_ERROR_WORKER = 2;
const PARSE_ERROR_NO_ARTICLE = 3;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.importGlobalProperties(["XMLHttpRequest"]);
XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils", "resource://services-common/utils.js");
+XPCOMUtils.defineLazyModuleGetter(this, "Messaging", "resource://gre/modules/Messaging.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ReaderWorker", "resource://gre/modules/reader/ReaderWorker.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStopwatch", "resource://gre/modules/TelemetryStopwatch.jsm");
XPCOMUtils.defineLazyGetter(this, "Readability", function() {
let scope = {};
scope.dump = this.dump;
@@ -280,17 +281,27 @@ this.ReaderMode = {
* @return {Promise}
* @resolves When the article is stored.
* @rejects OS.File.Error
*/
storeArticleInCache: Task.async(function* (article) {
let array = new TextEncoder().encode(JSON.stringify(article));
let path = this._toHashedPath(article.url);
yield this._ensureCacheDir();
- yield OS.File.writeAtomic(path, array, { tmpPath: path + ".tmp" });
+ return OS.File.writeAtomic(path, array, { tmpPath: path + ".tmp" })
+ .then(success => {
+ OS.File.stat(path).then(info => {
+ return Messaging.sendRequest({
+ type: "Reader:AddedToCache",
+ url: article.url,
+ size: info.size,
+ path: path,
+ });
+ });
+ });
}),
/**
* Removes an article from the cache given an article URI.
*
* @param url The article URL.
* @return {Promise}
* @resolves When the article is removed.