mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 750712 - Add about:reader special url for reader mode pages (r=mfinkle)
This commit is contained in:
parent
7d13aab294
commit
2e309072a5
1285
mobile/android/chrome/content/Readability.js
Normal file
1285
mobile/android/chrome/content/Readability.js
Normal file
File diff suppressed because it is too large
Load Diff
24
mobile/android/chrome/content/aboutReader.html
Normal file
24
mobile/android/chrome/content/aboutReader.html
Normal file
@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title></title>
|
||||
<meta content="text/html; charset=UTF-8" http-equiv="content-type">
|
||||
<meta name="viewport" content="width=480; initial-scale=.6667; user-scalable=0" />
|
||||
|
||||
<link rel="icon" type="image/png" href="chrome://branding/content/favicon32.png" />
|
||||
<link rel="stylesheet" href="chrome://browser/skin/aboutReader.css" type="text/css"/>
|
||||
</head>
|
||||
|
||||
<body onload="AboutReader.init();" onunload="AboutReader.uninit();">
|
||||
<div id="reader-header" class="header">
|
||||
</div>
|
||||
|
||||
<div id="reader-content" class="content">
|
||||
</div>
|
||||
|
||||
<script type="application/javascript;version=1.8" src="chrome://browser/content/aboutReader.js">
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
120
mobile/android/chrome/content/aboutReader.js
Normal file
120
mobile/android/chrome/content/aboutReader.js
Normal file
@ -0,0 +1,120 @@
|
||||
/* 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/. */
|
||||
|
||||
let Ci = Components.interfaces, Cc = Components.classes, Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm")
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(window, "gChromeWin", function ()
|
||||
window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShellTreeItem)
|
||||
.rootTreeItem
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindow)
|
||||
.QueryInterface(Ci.nsIDOMChromeWindow));
|
||||
|
||||
function dump(s) {
|
||||
Services.console.logStringMessage("Reader: " + s);
|
||||
}
|
||||
|
||||
let gStrings = Services.strings.createBundle("chrome://browser/locale/aboutReader.properties");
|
||||
|
||||
let AboutReader = {
|
||||
init: function Reader_init() {
|
||||
dump("Init()");
|
||||
|
||||
dump("Feching header and content notes from about:reader");
|
||||
this._titleElement = document.getElementById("reader-header");
|
||||
this._contentElement = document.getElementById("reader-content");
|
||||
|
||||
dump("Decoding query arguments");
|
||||
let queryArgs = this._decodeQueryString(window.location.href);
|
||||
|
||||
let url = queryArgs.url;
|
||||
if (url) {
|
||||
dump("Fetching page with URL: " + url);
|
||||
this._loadFromURL(url);
|
||||
} else {
|
||||
var tabId = queryArgs.tabId;
|
||||
if (tabId) {
|
||||
dump("Loading from tab with ID: " + tabId);
|
||||
this._loadFromTab(tabId);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
uninit: function Reader_uninit() {
|
||||
this._hideContent();
|
||||
|
||||
delete this._titleElement;
|
||||
delete this._contentElement;
|
||||
},
|
||||
|
||||
_loadFromURL: function Reader_loadFromURL(url) {
|
||||
this._showProgress();
|
||||
|
||||
gChromeWin.Reader.parseDocumentFromURL(url, function(article) {
|
||||
if (article)
|
||||
this._showContent(article);
|
||||
else
|
||||
this._showError(gStrings.GetStringFromName("aboutReader.loadError"));
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
_loadFromTab: function Reader_loadFromTab(tabId) {
|
||||
this._showProgress();
|
||||
|
||||
gChromeWin.Reader.parseDocumentFromTab(tabId, function(article) {
|
||||
if (article)
|
||||
this._showContent(article);
|
||||
else
|
||||
this._showError(gStrings.GetStringFromName("aboutReader.loadError"));
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
_showError: function Reader_showError(error) {
|
||||
this._titleElement.style.display = "none";
|
||||
this._contentElement.innerHTML = error;
|
||||
this._contentElement.style.display = "block";
|
||||
|
||||
document.title = error;
|
||||
},
|
||||
|
||||
_showContent: function Reader_showContent(article) {
|
||||
this._titleElement.innerHTML = article.title;
|
||||
this._titleElement.style.display = "block";
|
||||
|
||||
this._contentElement.innerHTML = article.content;
|
||||
this._contentElement.style.display = "block";
|
||||
|
||||
document.title = article.title;
|
||||
},
|
||||
|
||||
_hideContent: function Reader_hideContent() {
|
||||
this._titleElement.style.display = "none";
|
||||
this._contentElement.style.display = "none";
|
||||
},
|
||||
|
||||
_showProgress: function Reader_showProgress() {
|
||||
this._titleElement.style.display = "none";
|
||||
this._contentElement.innerHTML = gStrings.GetStringFromName("aboutReader.loading");
|
||||
this._contentElement.style.display = "block";
|
||||
},
|
||||
|
||||
_decodeQueryString: function Reader_decodeQueryString(url) {
|
||||
let result = {};
|
||||
let query = url.split("?")[1];
|
||||
if (query) {
|
||||
let pairs = query.split("&");
|
||||
for (let i = 0; i < pairs.length; i++) {
|
||||
let [name, value] = pairs[i].split("=");
|
||||
result[name] = decodeURIComponent(value);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
@ -30,6 +30,7 @@ XPCOMUtils.defineLazyGetter(this, "DebuggerServer", function() {
|
||||
// Lazily-loaded browser scripts:
|
||||
[
|
||||
["SelectHelper", "chrome://browser/content/SelectHelper.js"],
|
||||
["Readability", "chrome://browser/content/Readability.js"],
|
||||
].forEach(function (aScript) {
|
||||
let [name, script] = aScript;
|
||||
XPCOMUtils.defineLazyGetter(window, name, function() {
|
||||
@ -206,6 +207,7 @@ var BrowserApp = {
|
||||
ActivityObserver.init();
|
||||
WebappsUI.init();
|
||||
RemoteDebugger.init();
|
||||
Reader.init();
|
||||
#ifdef ACCESSIBILITY
|
||||
AccessFu.attach(window);
|
||||
#endif
|
||||
@ -407,6 +409,7 @@ var BrowserApp = {
|
||||
SearchEngines.uninit();
|
||||
WebappsUI.uninit();
|
||||
RemoteDebugger.uninit();
|
||||
Reader.uninit();
|
||||
},
|
||||
|
||||
// This function returns false during periods where the browser displayed document is
|
||||
@ -5338,3 +5341,288 @@ var RemoteDebugger = {
|
||||
dump("Remote debugger stopped");
|
||||
}
|
||||
}
|
||||
|
||||
let Reader = {
|
||||
// Version of the cache database schema
|
||||
DB_VERSION: 1,
|
||||
|
||||
DEBUG: 1,
|
||||
|
||||
init: function Reader_init() {
|
||||
this.log("Init()");
|
||||
this._requests = {};
|
||||
},
|
||||
|
||||
parseDocumentFromURL: function Reader_parseDocumentFromURL(url, callback) {
|
||||
// If there's an on-going request for the same URL, simply append one
|
||||
// more callback to it to be called when the request is done.
|
||||
if (url in this._requests) {
|
||||
let request = this._requests[url];
|
||||
request.callbacks.push(callback);
|
||||
return;
|
||||
}
|
||||
|
||||
let request = { url: url, callbacks: [callback] };
|
||||
this._requests[url] = request;
|
||||
|
||||
try {
|
||||
this.log("parseDocumentFromURL: " + url);
|
||||
|
||||
// First, try to find a cached parsed article in the DB
|
||||
this.getArticleFromCache(url, function(article) {
|
||||
if (article) {
|
||||
this.log("Page found in cache, return article immediately");
|
||||
this._runCallbacksAndFinish(request, article);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._requests) {
|
||||
this.log("Reader has been destroyed, abort");
|
||||
return;
|
||||
}
|
||||
|
||||
// Article hasn't been found in the cache DB, we need to
|
||||
// download the page and parse the article out of it.
|
||||
this._downloadAndParseDocument(url, request);
|
||||
}.bind(this));
|
||||
} catch (e) {
|
||||
this.log("Error parsing document from URL: " + e);
|
||||
this._runCallbacksAndFinish(request, null);
|
||||
}
|
||||
},
|
||||
|
||||
parseDocumentFromTab: function(tabId, callback) {
|
||||
try {
|
||||
this.log("parseDocumentFromTab: " + tabId);
|
||||
|
||||
let tab = BrowserApp.getTabForId(tabId);
|
||||
let url = tab.browser.contentWindow.location.href;
|
||||
|
||||
// First, try to find a cached parsed article in the DB
|
||||
this.getArticleFromCache(url, function(article) {
|
||||
if (article) {
|
||||
this.log("Page found in cache, return article immediately");
|
||||
callback(article);
|
||||
return;
|
||||
}
|
||||
|
||||
// We need to clone the document before parsing because readability
|
||||
// changes the document object in several ways to find the article
|
||||
// in it.
|
||||
let doc = tab.browser.contentWindow.document.cloneNode(true);
|
||||
let uri = Services.io.newURI(url, null, null);
|
||||
|
||||
let readability = new Readability(uri, doc);
|
||||
let article = readability.parse();
|
||||
|
||||
if (!article) {
|
||||
this.log("Failed to parse page");
|
||||
callback(null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Append URL to the article data
|
||||
article.url = url;
|
||||
|
||||
callback(article);
|
||||
}.bind(this));
|
||||
} catch (e) {
|
||||
this.log("Error parsing document from tab: " + e);
|
||||
callback(null);
|
||||
}
|
||||
},
|
||||
|
||||
getArticleFromCache: function Reader_getArticleFromCache(url, callback) {
|
||||
this._getCacheDB(function(cacheDB) {
|
||||
if (!cacheDB) {
|
||||
callback(false);
|
||||
return;
|
||||
}
|
||||
|
||||
let transaction = cacheDB.transaction(cacheDB.objectStoreNames);
|
||||
let articles = transaction.objectStore(cacheDB.objectStoreNames[0]);
|
||||
|
||||
let request = articles.get(url);
|
||||
|
||||
request.onerror = function(event) {
|
||||
this.log("Error getting article from the cache DB: " + url);
|
||||
callback(null);
|
||||
}.bind(this);
|
||||
|
||||
request.onsuccess = function(event) {
|
||||
this.log("Got article from the cache DB: " + event.target.result);
|
||||
callback(event.target.result);
|
||||
}.bind(this);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
storeArticleInCache: function Reader_storeArticleInCache(article, callback) {
|
||||
this._getCacheDB(function(cacheDB) {
|
||||
if (!cacheDB) {
|
||||
callback(false);
|
||||
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);
|
||||
callback(false);
|
||||
}.bind(this);
|
||||
|
||||
request.onsuccess = function(event) {
|
||||
this.log("Stored article in the cache DB: " + article.url);
|
||||
callback(true);
|
||||
}.bind(this);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
uninit: function Reader_uninit() {
|
||||
let requests = this._requests;
|
||||
for (let url in requests) {
|
||||
let request = requests[url];
|
||||
if (request.browser) {
|
||||
let browser = request.browser;
|
||||
browser.parentNode.removeChild(browser);
|
||||
}
|
||||
}
|
||||
delete this._requests;
|
||||
|
||||
if (this._cacheDB) {
|
||||
this._cacheDB.close();
|
||||
delete this._cacheDB;
|
||||
}
|
||||
},
|
||||
|
||||
log: function(msg) {
|
||||
if (this.DEBUG)
|
||||
dump("Reader: " + msg);
|
||||
},
|
||||
|
||||
_runCallbacksAndFinish: function Reader_runCallbacksAndFinish(request, result) {
|
||||
delete this._requests[request.url];
|
||||
|
||||
request.callbacks.forEach(function(callback) {
|
||||
callback(result);
|
||||
});
|
||||
},
|
||||
|
||||
_dowloadDocument: function Reader_downloadDocument(url, callback) {
|
||||
// We want to parse those arbitrary pages safely, outside the privileged
|
||||
// context of chrome. We create a hidden browser element to fetch the
|
||||
// loaded page's document object then discard the browser element.
|
||||
|
||||
let browser = document.createElement("browser");
|
||||
browser.setAttribute("type", "content");
|
||||
browser.setAttribute("collapsed", "true");
|
||||
|
||||
document.documentElement.appendChild(browser);
|
||||
browser.stop();
|
||||
|
||||
browser.webNavigation.allowAuth = false;
|
||||
browser.webNavigation.allowImages = false;
|
||||
browser.webNavigation.allowJavascript = false;
|
||||
browser.webNavigation.allowMetaRedirects = true;
|
||||
browser.webNavigation.allowPlugins = false;
|
||||
|
||||
browser.addEventListener("DOMContentLoaded", function (event) {
|
||||
let doc = event.originalTarget;
|
||||
this.log("Done loading: " + doc);
|
||||
if (doc.location.href == "about:blank" || doc.defaultView.frameElement) {
|
||||
callback(null);
|
||||
|
||||
// Request has finished with error, remove browser element
|
||||
browser.parentNode.removeChild(browser);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(doc);
|
||||
|
||||
// Request has finished, remove browser element
|
||||
browser.parentNode.removeChild(browser);
|
||||
}.bind(this));
|
||||
|
||||
browser.loadURIWithFlags(url, Ci.nsIWebNavigation.LOAD_FLAGS_NONE,
|
||||
null, null, null);
|
||||
|
||||
return browser;
|
||||
},
|
||||
|
||||
_downloadAndParseDocument: function Reader_downloadAndParseDocument(url, request) {
|
||||
try {
|
||||
this.log("Needs to fetch page, creating request: " + url);
|
||||
|
||||
request.browser = this._dowloadDocument(url, function(doc) {
|
||||
this.log("Finished loading page: " + doc);
|
||||
|
||||
// Delete reference to the browser element as we're
|
||||
// now done with this request.
|
||||
delete request.browser;
|
||||
|
||||
if (!doc) {
|
||||
this.log("Error loading page");
|
||||
this._runCallbacksAndFinish(request, null);
|
||||
}
|
||||
|
||||
this.log("Parsing response with Readability");
|
||||
|
||||
let uri = Services.io.newURI(url, null, null);
|
||||
let readability = new Readability(uri, doc);
|
||||
let article = readability.parse();
|
||||
|
||||
if (!article) {
|
||||
this.log("Failed to parse page");
|
||||
this._runCallbacksAndFinish(request, null);
|
||||
return;
|
||||
}
|
||||
|
||||
this.log("Parsing has been successful");
|
||||
|
||||
// Append URL to the article data
|
||||
article.url = url;
|
||||
|
||||
this._runCallbacksAndFinish(request, article);
|
||||
}.bind(this));
|
||||
} catch (e) {
|
||||
this.log("Error downloading and parsing document: " + e);
|
||||
this._runCallbacksAndFinish(request, null);
|
||||
}
|
||||
},
|
||||
|
||||
_getCacheDB: function Reader_getCacheDB(callback) {
|
||||
if (this._cacheDB) {
|
||||
callback(this._cacheDB);
|
||||
return;
|
||||
}
|
||||
|
||||
let request = window.mozIndexedDB.open("about:reader", this.DB_VERSION);
|
||||
|
||||
request.onerror = function(event) {
|
||||
this.log("Error connecting to the cache DB");
|
||||
this._cacheDB = null;
|
||||
callback(null);
|
||||
}.bind(this);
|
||||
|
||||
request.onsuccess = function(event) {
|
||||
this.log("Successfully connected to the cache DB");
|
||||
this._cacheDB = event.target.result;
|
||||
callback(this._cacheDB);
|
||||
}.bind(this);
|
||||
|
||||
request.onupgradeneeded = function(event) {
|
||||
this.log("Database schema upgrade from " +
|
||||
event.oldVersion + " to " + event.newVersion);
|
||||
|
||||
let cacheDB = event.target.result;
|
||||
|
||||
// Create the articles object store
|
||||
this.log("Creating articles object store");
|
||||
cacheDB.createObjectStore("articles", { keyPath: "url" });
|
||||
|
||||
this.log("Database upgrade done: " + this.DB_VERSION);
|
||||
}.bind(this);
|
||||
}
|
||||
};
|
||||
|
@ -14,6 +14,9 @@ chrome.jar:
|
||||
content/aboutCertError.xhtml (content/aboutCertError.xhtml)
|
||||
content/aboutDownloads.xhtml (content/aboutDownloads.xhtml)
|
||||
content/aboutDownloads.js (content/aboutDownloads.js)
|
||||
content/aboutReader.html (content/aboutReader.html)
|
||||
content/aboutReader.js (content/aboutReader.js)
|
||||
content/Readability.js (content/Readability.js)
|
||||
content/aboutHome.xhtml (content/aboutHome.xhtml)
|
||||
* content/aboutRights.xhtml (content/aboutRights.xhtml)
|
||||
* content/aboutApps.xhtml (content/aboutApps.xhtml)
|
||||
|
@ -58,6 +58,10 @@ let modules = {
|
||||
downloads: {
|
||||
uri: "chrome://browser/content/aboutDownloads.xhtml",
|
||||
privileged: true
|
||||
},
|
||||
reader: {
|
||||
uri: "chrome://browser/content/aboutReader.html",
|
||||
privileged: true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@ contract @mozilla.org/network/protocol/about;1?what=certerror {322ba47e-7047-4f7
|
||||
contract @mozilla.org/network/protocol/about;1?what=home {322ba47e-7047-4f71-aebf-cb7d69325cd9}
|
||||
contract @mozilla.org/network/protocol/about;1?what=apps {322ba47e-7047-4f71-aebf-cb7d69325cd9}
|
||||
contract @mozilla.org/network/protocol/about;1?what=downloads {322ba47e-7047-4f71-aebf-cb7d69325cd9}
|
||||
contract @mozilla.org/network/protocol/about;1?what=reader {322ba47e-7047-4f71-aebf-cb7d69325cd9}
|
||||
#ifdef MOZ_SAFE_BROWSING
|
||||
contract @mozilla.org/network/protocol/about;1?what=blocked {322ba47e-7047-4f71-aebf-cb7d69325cd9}
|
||||
#endif
|
||||
|
@ -0,0 +1,6 @@
|
||||
# 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/.
|
||||
|
||||
aboutReader.loading=Loading...
|
||||
aboutReader.loadError=Failed to load article from page
|
@ -14,6 +14,7 @@
|
||||
locale/@AB_CD@/browser/aboutCertError.dtd (%chrome/aboutCertError.dtd)
|
||||
locale/@AB_CD@/browser/aboutDownloads.dtd (%chrome/aboutDownloads.dtd)
|
||||
locale/@AB_CD@/browser/aboutDownloads.properties (%chrome/aboutDownloads.properties)
|
||||
locale/@AB_CD@/browser/aboutReader.properties (%chrome/aboutReader.properties)
|
||||
locale/@AB_CD@/browser/browser.properties (%chrome/browser.properties)
|
||||
locale/@AB_CD@/browser/config.dtd (%chrome/config.dtd)
|
||||
locale/@AB_CD@/browser/config.properties (%chrome/config.properties)
|
||||
|
37
mobile/android/themes/core/aboutReader.css
Normal file
37
mobile/android/themes/core/aboutReader.css
Normal file
@ -0,0 +1,37 @@
|
||||
/* 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/. */
|
||||
|
||||
%filter substitution
|
||||
%include defines.inc
|
||||
|
||||
html {
|
||||
font-family: "Droid Sans",helvetica,arial,clean,sans-serif;
|
||||
font-size: 24px;
|
||||
background: #FFFFFF;
|
||||
-moz-text-size-adjust: none;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
.header {
|
||||
width: 100%;
|
||||
color: black;
|
||||
text-align: center;
|
||||
font-size: 30px;
|
||||
font-weight: bold;
|
||||
padding-top: 30px;
|
||||
padding-bottom: 30px;
|
||||
border-bottom: 4px solid;
|
||||
-moz-border-bottom-colors: #ff9100 #f27900;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.content {
|
||||
font-family: "Droid Serif",serif;
|
||||
padding-top: 30px;
|
||||
padding-bottom: 30px;
|
||||
display: none;
|
||||
}
|
@ -11,6 +11,7 @@ chrome.jar:
|
||||
skin/aboutAddons.css (aboutAddons.css)
|
||||
skin/aboutApps.css (aboutApps.css)
|
||||
* skin/aboutDownloads.css (aboutDownloads.css)
|
||||
* skin/aboutReader.css (aboutReader.css)
|
||||
* skin/browser.css (browser.css)
|
||||
* skin/content.css (content.css)
|
||||
skin/config.css (config.css)
|
||||
|
Loading…
Reference in New Issue
Block a user