mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1007409 - Cache reading list articles in files, not indexedDB. r=rnewman
This commit is contained in:
parent
2683d1a61a
commit
c1da8b2813
@ -4,9 +4,12 @@
|
|||||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils",
|
||||||
|
"resource://services-common/utils.js");
|
||||||
|
|
||||||
let Reader = {
|
let Reader = {
|
||||||
// Version of the cache database schema
|
// Version of the cache schema.
|
||||||
DB_VERSION: 1,
|
CACHE_VERSION: 1,
|
||||||
|
|
||||||
DEBUG: 0,
|
DEBUG: 0,
|
||||||
|
|
||||||
@ -76,7 +79,8 @@ let Reader = {
|
|||||||
observe: function(aMessage, aTopic, aData) {
|
observe: function(aMessage, aTopic, aData) {
|
||||||
switch(aTopic) {
|
switch(aTopic) {
|
||||||
case "Reader:Removed": {
|
case "Reader:Removed": {
|
||||||
this.removeArticleFromCache(aData);
|
let uri = Services.io.newURI(aData, null, null);
|
||||||
|
this.removeArticleFromCache(uri).catch(e => Cu.reportError("Error removing article from cache: " + e));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,10 +95,13 @@ let Reader = {
|
|||||||
|
|
||||||
_addTabToReadingList: function(tabID) {
|
_addTabToReadingList: function(tabID) {
|
||||||
let tab = BrowserApp.getTabForId(tabID);
|
let tab = BrowserApp.getTabForId(tabID);
|
||||||
let currentURI = tab.browser.currentURI;
|
if (!tab) {
|
||||||
let urlWithoutRef = currentURI.specIgnoringRef;
|
Cu.reportError("Can't add tab to reading list because no tab found for ID: " + tabID);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let uri = tab.browser.currentURI;
|
||||||
|
|
||||||
this.getArticleFromCache(urlWithoutRef, (article) => {
|
this.getArticleFromCache(uri).then(article => {
|
||||||
// If the article is already in the cache, just use that.
|
// If the article is already in the cache, just use that.
|
||||||
if (article) {
|
if (article) {
|
||||||
this.addArticleToReadingList(article);
|
this.addArticleToReadingList(article);
|
||||||
@ -102,7 +109,7 @@ let Reader = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, get the article data from the tab.
|
// Otherwise, get the article data from the tab.
|
||||||
this.getArticleForTab(tabID, urlWithoutRef, (article) => {
|
this.getArticleForTab(tabID, uri.specIgnoringRef, article => {
|
||||||
if (article) {
|
if (article) {
|
||||||
this.addArticleToReadingList(article);
|
this.addArticleToReadingList(article);
|
||||||
} else {
|
} else {
|
||||||
@ -114,7 +121,7 @@ let Reader = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
}, e => Cu.reportError("Error trying to get article from cache: " + e));
|
||||||
},
|
},
|
||||||
|
|
||||||
addArticleToReadingList: function(article) {
|
addArticleToReadingList: function(article) {
|
||||||
@ -131,7 +138,7 @@ let Reader = {
|
|||||||
excerpt: article.excerpt || "",
|
excerpt: article.excerpt || "",
|
||||||
});
|
});
|
||||||
|
|
||||||
this.storeArticleInCache(article);
|
this.storeArticleInCache(article).catch(e => Cu.reportError("Error storing article in cache: " + e));
|
||||||
},
|
},
|
||||||
|
|
||||||
getStateForParseOnLoad: function Reader_getStateForParseOnLoad() {
|
getStateForParseOnLoad: function Reader_getStateForParseOnLoad() {
|
||||||
@ -154,30 +161,28 @@ let Reader = {
|
|||||||
let request = { url: url, callbacks: [callback] };
|
let request = { url: url, callbacks: [callback] };
|
||||||
this._requests[url] = request;
|
this._requests[url] = request;
|
||||||
|
|
||||||
try {
|
let uri = Services.io.newURI(url, null, null);
|
||||||
this.log("parseDocumentFromURL: " + url);
|
|
||||||
|
|
||||||
// First, try to find a cached parsed article in the DB
|
// First, try to find a parsed article in the cache.
|
||||||
this.getArticleFromCache(url, function(article) {
|
this.getArticleFromCache(uri).then(article => {
|
||||||
if (article) {
|
if (article) {
|
||||||
this.log("Page found in cache, return article immediately");
|
this.log("Page found in cache, return article immediately");
|
||||||
this._runCallbacksAndFinish(request, article);
|
this._runCallbacksAndFinish(request, article);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this._requests) {
|
if (!this._requests) {
|
||||||
this.log("Reader has been destroyed, abort");
|
this.log("Reader has been destroyed, abort");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Article hasn't been found in the cache DB, we need to
|
// Article hasn't been found in the cache, we need to
|
||||||
// download the page and parse the article out of it.
|
// download the page and parse the article out of it.
|
||||||
this._downloadAndParseDocument(url, request);
|
this._downloadAndParseDocument(url, request);
|
||||||
}.bind(this));
|
}, e => {
|
||||||
} catch (e) {
|
Cu.reportError("Error trying to get article from cache: " + e);
|
||||||
this.log("Error parsing document from URL: " + e);
|
|
||||||
this._runCallbacksAndFinish(request, null);
|
this._runCallbacksAndFinish(request, null);
|
||||||
}
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
getArticleForTab: function Reader_getArticleForTab(tabId, url, callback) {
|
getArticleForTab: function Reader_getArticleForTab(tabId, url, callback) {
|
||||||
@ -194,109 +199,81 @@ let Reader = {
|
|||||||
this.parseDocumentFromURL(url, callback);
|
this.parseDocumentFromURL(url, callback);
|
||||||
},
|
},
|
||||||
|
|
||||||
parseDocumentFromTab: function(tabId, callback) {
|
parseDocumentFromTab: function (tab, callback) {
|
||||||
try {
|
let uri = tab.browser.currentURI;
|
||||||
this.log("parseDocumentFromTab: " + tabId);
|
if (!this._shouldCheckUri(uri)) {
|
||||||
|
callback(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let tab = BrowserApp.getTabForId(tabId);
|
// First, try to find a parsed article in the cache.
|
||||||
let url = tab.browser.contentWindow.location.href;
|
this.getArticleFromCache(uri).then(article => {
|
||||||
let uri = Services.io.newURI(url, null, null);
|
if (article) {
|
||||||
|
this.log("Page found in cache, return article immediately");
|
||||||
if (!this._shouldCheckUri(uri)) {
|
callback(article);
|
||||||
callback(null);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// First, try to find a cached parsed article in the DB
|
let doc = tab.browser.contentWindow.document;
|
||||||
this.getArticleFromCache(url, function(article) {
|
this._readerParse(uri, doc, article => {
|
||||||
if (article) {
|
if (!article) {
|
||||||
this.log("Page found in cache, return article immediately");
|
this.log("Failed to parse page");
|
||||||
callback(article);
|
callback(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
callback(article);
|
||||||
let doc = tab.browser.contentWindow.document;
|
});
|
||||||
this._readerParse(uri, doc, function (article) {
|
}, e => {
|
||||||
if (!article) {
|
Cu.reportError("Error trying to get article from cache: " + e);
|
||||||
this.log("Failed to parse page");
|
|
||||||
callback(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(article);
|
|
||||||
}.bind(this));
|
|
||||||
}.bind(this));
|
|
||||||
} catch (e) {
|
|
||||||
this.log("Error parsing document from tab: " + e);
|
|
||||||
callback(null);
|
callback(null);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves an article from the cache given an article URI.
|
||||||
|
*
|
||||||
|
* @param uri The article URI.
|
||||||
|
* @return Promise
|
||||||
|
* @resolve JS object representing the article, or null if no article is found.
|
||||||
|
* @rejects OS.File.Error
|
||||||
|
*/
|
||||||
|
getArticleFromCache: Task.async(function* (uri) {
|
||||||
|
let path = this._toHashedPath(uri.specIgnoringRef);
|
||||||
|
try {
|
||||||
|
let array = yield OS.File.read(path);
|
||||||
|
return JSON.parse(new TextDecoder().decode(array));
|
||||||
|
} catch (e if e instanceof OS.File.Error && e.becauseNoSuchFile) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
},
|
}),
|
||||||
|
|
||||||
getArticleFromCache: function Reader_getArticleFromCache(url, callback) {
|
/**
|
||||||
this._getCacheDB(function(cacheDB) {
|
* Stores an article in the cache.
|
||||||
if (!cacheDB) {
|
*
|
||||||
callback(false);
|
* @param article JS object representing article.
|
||||||
return;
|
* @return Promise
|
||||||
}
|
* @resolve 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" });
|
||||||
|
}),
|
||||||
|
|
||||||
let transaction = cacheDB.transaction(cacheDB.objectStoreNames);
|
/**
|
||||||
let articles = transaction.objectStore(cacheDB.objectStoreNames[0]);
|
* Removes an article from the cache given an article URI.
|
||||||
|
*
|
||||||
let request = articles.get(url);
|
* @param uri The article URI.
|
||||||
|
* @return Promise
|
||||||
request.onerror = function(event) {
|
* @resolve When the article is removed.
|
||||||
this.log("Error getting article from the cache DB: " + url);
|
* @rejects OS.File.Error
|
||||||
callback(null);
|
*/
|
||||||
}.bind(this);
|
removeArticleFromCache: Task.async(function* (uri) {
|
||||||
|
let path = this._toHashedPath(uri.specIgnoringRef);
|
||||||
request.onsuccess = function(event) {
|
yield OS.File.remove(path);
|
||||||
this.log("Got article from the cache DB: " + event.target.result);
|
}),
|
||||||
callback(event.target.result);
|
|
||||||
}.bind(this);
|
|
||||||
}.bind(this));
|
|
||||||
},
|
|
||||||
|
|
||||||
storeArticleInCache: function Reader_storeArticleInCache(article) {
|
|
||||||
this._getCacheDB(function(cacheDB) {
|
|
||||||
if (!cacheDB) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let transaction = cacheDB.transaction(cacheDB.objectStoreNames, "readwrite");
|
|
||||||
let articles = transaction.objectStore(cacheDB.objectStoreNames[0]);
|
|
||||||
|
|
||||||
let request = articles.add(article);
|
|
||||||
|
|
||||||
request.onerror = function(event) {
|
|
||||||
this.log("Error storing article in the cache DB: " + article.url);
|
|
||||||
}.bind(this);
|
|
||||||
|
|
||||||
request.onsuccess = function(event) {
|
|
||||||
this.log("Stored article in the cache DB: " + article.url);
|
|
||||||
}.bind(this);
|
|
||||||
}.bind(this));
|
|
||||||
},
|
|
||||||
|
|
||||||
removeArticleFromCache: function Reader_removeArticleFromCache(url) {
|
|
||||||
this._getCacheDB(function(cacheDB) {
|
|
||||||
if (!cacheDB) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let transaction = cacheDB.transaction(cacheDB.objectStoreNames, "readwrite");
|
|
||||||
let articles = transaction.objectStore(cacheDB.objectStoreNames[0]);
|
|
||||||
|
|
||||||
let request = articles.delete(url);
|
|
||||||
|
|
||||||
request.onerror = function(event) {
|
|
||||||
this.log("Error removing article from the cache DB: " + url);
|
|
||||||
}.bind(this);
|
|
||||||
|
|
||||||
request.onsuccess = function(event) {
|
|
||||||
this.log("Removed article from the cache DB: " + url);
|
|
||||||
}.bind(this);
|
|
||||||
}.bind(this));
|
|
||||||
},
|
|
||||||
|
|
||||||
uninit: function Reader_uninit() {
|
uninit: function Reader_uninit() {
|
||||||
Services.prefs.removeObserver("reader.parse-on-load.", this);
|
Services.prefs.removeObserver("reader.parse-on-load.", this);
|
||||||
@ -312,11 +289,6 @@ let Reader = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
delete this._requests;
|
delete this._requests;
|
||||||
|
|
||||||
if (this._cacheDB) {
|
|
||||||
this._cacheDB.close();
|
|
||||||
delete this._cacheDB;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
log: function(msg) {
|
log: function(msg) {
|
||||||
@ -374,7 +346,7 @@ let Reader = {
|
|||||||
doc: new XMLSerializer().serializeToString(doc)
|
doc: new XMLSerializer().serializeToString(doc)
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
dump("Reader: could not build Readability arguments: " + e);
|
Cu.reportError("Reader: could not build Readability arguments: " + e);
|
||||||
callback(null);
|
callback(null);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -406,7 +378,7 @@ let Reader = {
|
|||||||
browser.webNavigation.allowMetaRedirects = true;
|
browser.webNavigation.allowMetaRedirects = true;
|
||||||
browser.webNavigation.allowPlugins = false;
|
browser.webNavigation.allowPlugins = false;
|
||||||
|
|
||||||
browser.addEventListener("DOMContentLoaded", function (event) {
|
browser.addEventListener("DOMContentLoaded", event => {
|
||||||
let doc = event.originalTarget;
|
let doc = event.originalTarget;
|
||||||
|
|
||||||
// ignore on frames and other documents
|
// ignore on frames and other documents
|
||||||
@ -423,7 +395,7 @@ let Reader = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
callback(doc);
|
callback(doc);
|
||||||
}.bind(this));
|
});
|
||||||
|
|
||||||
browser.loadURIWithFlags(url, Ci.nsIWebNavigation.LOAD_FLAGS_NONE,
|
browser.loadURIWithFlags(url, Ci.nsIWebNavigation.LOAD_FLAGS_NONE,
|
||||||
null, null, null);
|
null, null, null);
|
||||||
@ -435,7 +407,7 @@ let Reader = {
|
|||||||
try {
|
try {
|
||||||
this.log("Needs to fetch page, creating request: " + url);
|
this.log("Needs to fetch page, creating request: " + url);
|
||||||
|
|
||||||
request.browser = this._downloadDocument(url, function(doc) {
|
request.browser = this._downloadDocument(url, doc => {
|
||||||
this.log("Finished loading page: " + doc);
|
this.log("Finished loading page: " + doc);
|
||||||
|
|
||||||
if (!doc) {
|
if (!doc) {
|
||||||
@ -447,7 +419,7 @@ let Reader = {
|
|||||||
this.log("Parsing response with Readability");
|
this.log("Parsing response with Readability");
|
||||||
|
|
||||||
let uri = Services.io.newURI(url, null, null);
|
let uri = Services.io.newURI(url, null, null);
|
||||||
this._readerParse(uri, doc, function (article) {
|
this._readerParse(uri, doc, article => {
|
||||||
// Delete reference to the browser element as we've finished parsing.
|
// Delete reference to the browser element as we've finished parsing.
|
||||||
let browser = request.browser;
|
let browser = request.browser;
|
||||||
if (browser) {
|
if (browser) {
|
||||||
@ -462,47 +434,57 @@ let Reader = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.log("Parsing has been successful");
|
this.log("Parsing has been successful");
|
||||||
|
|
||||||
this._runCallbacksAndFinish(request, article);
|
this._runCallbacksAndFinish(request, article);
|
||||||
}.bind(this));
|
});
|
||||||
}.bind(this));
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.log("Error downloading and parsing document: " + e);
|
this.log("Error downloading and parsing document: " + e);
|
||||||
this._runCallbacksAndFinish(request, null);
|
this._runCallbacksAndFinish(request, null);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_getCacheDB: function Reader_getCacheDB(callback) {
|
get _cryptoHash() {
|
||||||
if (this._cacheDB) {
|
delete this._cryptoHash;
|
||||||
callback(this._cacheDB);
|
return this._cryptoHash = Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash);
|
||||||
return;
|
},
|
||||||
}
|
|
||||||
|
|
||||||
let request = window.indexedDB.open("about:reader", this.DB_VERSION);
|
get _unicodeConverter() {
|
||||||
|
delete this._unicodeConverter;
|
||||||
|
this._unicodeConverter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
|
||||||
|
.createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||||
|
this._unicodeConverter.charset = "utf8";
|
||||||
|
return this._unicodeConverter;
|
||||||
|
},
|
||||||
|
|
||||||
request.onerror = function(event) {
|
/**
|
||||||
this.log("Error connecting to the cache DB");
|
* Calculate the hashed path for a stripped article URL.
|
||||||
this._cacheDB = null;
|
*
|
||||||
callback(null);
|
* @param url The article URL. This should have referrers removed.
|
||||||
}.bind(this);
|
* @return The file path to the cached article.
|
||||||
|
*/
|
||||||
|
_toHashedPath: function (url) {
|
||||||
|
let value = this._unicodeConverter.convertToByteArray(url);
|
||||||
|
this._cryptoHash.init(this._cryptoHash.MD5);
|
||||||
|
this._cryptoHash.update(value, value.length);
|
||||||
|
|
||||||
request.onsuccess = function(event) {
|
let hash = CommonUtils.encodeBase32(this._cryptoHash.finish(false));
|
||||||
this.log("Successfully connected to the cache DB");
|
let fileName = hash.substring(0, hash.indexOf("=")) + ".json";
|
||||||
this._cacheDB = event.target.result;
|
return OS.Path.join(OS.Constants.Path.profileDir, "readercache", fileName);
|
||||||
callback(this._cacheDB);
|
},
|
||||||
}.bind(this);
|
|
||||||
|
|
||||||
request.onupgradeneeded = function(event) {
|
/**
|
||||||
this.log("Database schema upgrade from " +
|
* Ensures the cache directory exists.
|
||||||
event.oldVersion + " to " + event.newVersion);
|
*
|
||||||
|
* @return Promise
|
||||||
let cacheDB = event.target.result;
|
* @resolves When the cache directory exists.
|
||||||
|
* @rejects OS.File.Error
|
||||||
// Create the articles object store
|
*/
|
||||||
this.log("Creating articles object store");
|
_ensureCacheDir: function () {
|
||||||
cacheDB.createObjectStore("articles", { keyPath: "url" });
|
let dir = OS.Path.join(OS.Constants.Path.profileDir, "readercache");
|
||||||
|
return OS.File.exists(dir).then(exists => {
|
||||||
this.log("Database upgrade done: " + this.DB_VERSION);
|
if (!exists) {
|
||||||
}.bind(this);
|
return OS.File.makeDir(dir);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -4225,7 +4225,7 @@ Tab.prototype = {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Once document is fully loaded, parse it
|
// Once document is fully loaded, parse it
|
||||||
Reader.parseDocumentFromTab(this.id, function (article) {
|
Reader.parseDocumentFromTab(this, function (article) {
|
||||||
// The loaded page may have changed while we were parsing the document.
|
// The loaded page may have changed while we were parsing the document.
|
||||||
// Make sure we've got the current one.
|
// Make sure we've got the current one.
|
||||||
let uri = this.browser.currentURI;
|
let uri = this.browser.currentURI;
|
||||||
|
Loading…
Reference in New Issue
Block a user