Last active
August 29, 2015 14:14
-
-
Save evilpie/11c26923c67622a6c27f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# HG changeset patch | |
# Parent c6ae2f78d271f4bd87d8f0efb6086a8a624f0e4b | |
diff --git a/lib/contentPolicy.js b/lib/contentPolicy.js | |
--- a/lib/contentPolicy.js | |
+++ b/lib/contentPolicy.js | |
@@ -129,73 +129,92 @@ let Policy = exports.Policy = | |
Utils.styleService.loadAndRegisterSheet(collapseStyle, Ci.nsIStyleSheetService.USER_SHEET); | |
onShutdown.add(function() | |
{ | |
Utils.styleService.unregisterSheet(collapseStyle, Ci.nsIStyleSheetService.USER_SHEET); | |
}) | |
}, | |
/** | |
+ * Checks whether a window or its parent is whitelisted or has a valid site-key. | |
+ * @param wnd {nsIDOMWindow} | |
+ */ | |
+ shouldNeverBlockWindow: function(wnd) | |
+ { | |
+ let topWnd = wnd.top; | |
+ if (!topWnd || !topWnd.location || !topWnd.location.href) | |
+ return true; | |
+ | |
+ let uri = wnd.document.documentURIObject; | |
+ if (!uri) | |
+ return true; | |
+ | |
+ if (!Policy.isBlockableScheme(uri.scheme)) | |
+ return true; | |
+ | |
+ let [sitekey, sitekeyWnd] = getSitekey(wnd); | |
+ let testWnd = wnd; | |
+ let testSitekey = sitekey; | |
+ let testSitekeyWnd = sitekeyWnd; | |
+ let parentWndLocation = getWindowLocation(testWnd); | |
+ | |
+ while (true) | |
+ { | |
+ let testWndLocation = parentWndLocation; | |
+ parentWndLocation = (testWnd == testWnd.parent ? testWndLocation : getWindowLocation(testWnd.parent)); | |
+ match = Policy.isWhitelisted(testWndLocation, parentWndLocation, testSitekey); | |
+ | |
+ if (match instanceof WhitelistFilter) | |
+ { | |
+ FilterStorage.increaseHitCount(match, wnd); | |
+ RequestNotifier.addNodeData(testWnd.document, topWnd, Policy.type.DOCUMENT, getHostname(parentWndLocation), false, testWndLocation, match); | |
+ return true; | |
+ } | |
+ | |
+ if (testWnd.parent == testWnd) | |
+ break; | |
+ | |
+ if (testWnd == testSitekeyWnd) | |
+ [testSitekey, testSitekeyWnd] = getSitekey(testWnd.parent); | |
+ testWnd = testWnd.parent; | |
+ } | |
+ | |
+ return false; | |
+ }, | |
+ | |
+ /** | |
* Checks whether a node should be blocked, hides it if necessary | |
* @param wnd {nsIDOMWindow} | |
* @param node {nsIDOMElement} | |
* @param contentType {String} | |
* @param location {nsIURI} | |
* @param collapse {Boolean} true to force hiding of the node | |
* @return {Boolean} false if the node should be blocked | |
*/ | |
processNode: function(wnd, node, contentType, location, collapse) | |
{ | |
- let topWnd = wnd.top; | |
- if (!topWnd || !topWnd.location || !topWnd.location.href) | |
+ if (!wnd || Policy.shouldNeverBlockWindow(wnd)) | |
return true; | |
+ let topWnd = wnd.top; | |
let originWindow = Utils.getOriginWindow(wnd); | |
let wndLocation = originWindow.location.href; | |
let docDomain = getHostname(wndLocation); | |
- let match = null; | |
- let [sitekey, sitekeyWnd] = getSitekey(wnd); | |
- if (!match && Prefs.enabled) | |
- { | |
- let testWnd = wnd; | |
- let testSitekey = sitekey; | |
- let testSitekeyWnd = sitekeyWnd; | |
- let parentWndLocation = getWindowLocation(testWnd); | |
- while (true) | |
- { | |
- let testWndLocation = parentWndLocation; | |
- parentWndLocation = (testWnd == testWnd.parent ? testWndLocation : getWindowLocation(testWnd.parent)); | |
- match = Policy.isWhitelisted(testWndLocation, parentWndLocation, testSitekey); | |
- | |
- if (match instanceof WhitelistFilter) | |
- { | |
- FilterStorage.increaseHitCount(match, wnd); | |
- RequestNotifier.addNodeData(testWnd.document, topWnd, Policy.type.DOCUMENT, getHostname(parentWndLocation), false, testWndLocation, match); | |
- return true; | |
- } | |
- | |
- if (testWnd.parent == testWnd) | |
- break; | |
- | |
- if (testWnd == testSitekeyWnd) | |
- [testSitekey, testSitekeyWnd] = getSitekey(testWnd.parent); | |
- testWnd = testWnd.parent; | |
- } | |
- } | |
// Data loaded by plugins should be attached to the document | |
if (contentType == Policy.type.OBJECT_SUBREQUEST && node instanceof Ci.nsIDOMElement) | |
node = node.ownerDocument; | |
// Fix type for objects misrepresented as frames or images | |
if (contentType != Policy.type.OBJECT && (node instanceof Ci.nsIDOMHTMLObjectElement || node instanceof Ci.nsIDOMHTMLEmbedElement)) | |
contentType = Policy.type.OBJECT; | |
let locationText = location.spec; | |
- if (!match && contentType == Policy.type.ELEMHIDE) | |
+ let match = null; | |
+ if (contentType == Policy.type.ELEMHIDE) | |
{ | |
let testWnd = wnd; | |
let parentWndLocation = getWindowLocation(testWnd); | |
while (true) | |
{ | |
let testWndLocation = parentWndLocation; | |
parentWndLocation = (testWnd == testWnd.parent ? testWndLocation : getWindowLocation(testWnd.parent)); | |
let parentDocDomain = getHostname(parentWndLocation); | |
@@ -225,17 +244,17 @@ let Policy = exports.Policy = | |
{ | |
FilterStorage.increaseHitCount(exception, wnd); | |
RequestNotifier.addNodeData(node, topWnd, contentType, docDomain, false, locationText, exception); | |
return true; | |
} | |
} | |
let thirdParty = (contentType == Policy.type.ELEMHIDE ? false : isThirdParty(location, docDomain)); | |
- | |
+ let [sitekey, sitekeyWnd] = getSitekey(wnd); | |
if (!match && Prefs.enabled) | |
{ | |
match = defaultMatcher.matchesAny(locationText, Policy.typeDescr[contentType] || "", docDomain, thirdParty, sitekey); | |
if (match instanceof BlockingFilter && node.ownerDocument && !(contentType in Policy.nonVisual)) | |
{ | |
let prefCollapse = (match.collapse != null ? match.collapse : !Prefs.fastcollapse); | |
if (collapse || prefCollapse) | |
schedulePostProcess(node); | |
diff --git a/lib/elemHide.js b/lib/elemHide.js | |
--- a/lib/elemHide.js | |
+++ b/lib/elemHide.js | |
@@ -22,16 +22,17 @@ | |
Cu.import("resource://gre/modules/Services.jsm"); | |
let {Utils} = require("utils"); | |
let {IO} = require("io"); | |
let {Prefs} = require("prefs"); | |
let {ElemHideException} = require("filterClasses"); | |
let {FilterNotifier} = require("filterNotifier"); | |
let {AboutHandler} = require("elemHideHitRegistration"); | |
+let Poliy = null; | |
/** | |
* Lookup table, filters by their associated key | |
* @type Object | |
*/ | |
let filterByKey = Object.create(null); | |
/** | |
@@ -54,21 +55,35 @@ let exceptions = Object.create(null); | |
/** | |
* Currently applied stylesheet URL | |
* @type nsIURI | |
*/ | |
let styleURL = null; | |
/** | |
+ * Global stylesheet that should be loaded into content windows. | |
+ * @type nsIStyleSheet | |
+ */ | |
+let styleSheet = null; | |
+ | |
+/** | |
+ * Use the new way of injecting styles per window that exists since Firefox 33. | |
+ * @type boolean | |
+ */ | |
+let usePreloading = ('preloadSheet' in Utils.styleService); // XXX Disable when e10s is on | |
+ | |
+/** | |
* Element hiding component | |
* @class | |
*/ | |
let ElemHide = exports.ElemHide = | |
{ | |
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]), | |
+ | |
/** | |
* Indicates whether filters have been added or removed since the last apply() call. | |
* @type Boolean | |
*/ | |
isDirty: false, | |
/** | |
* Inidicates whether the element hiding stylesheet is currently applied. | |
@@ -76,31 +91,64 @@ let ElemHide = exports.ElemHide = | |
*/ | |
applied: false, | |
/** | |
* Called on module startup. | |
*/ | |
init: function() | |
{ | |
+ if (usePreloading) { | |
+ // Avoid dependency issue. | |
+ Policy = require("contentPolicy").Policy; | |
+ | |
+ Services.obs.addObserver(this, "content-document-global-created", true); | |
+ } | |
+ | |
Prefs.addListener(function(name) | |
{ | |
if (name == "enabled") | |
ElemHide.apply(); | |
}); | |
onShutdown.add(function() | |
{ | |
ElemHide.unapply(); | |
+ | |
+ if (usePreloading) | |
+ Services.obs.removeObserver(this, "content-document-global-created"); | |
}); | |
let styleFile = IO.resolveFilePath(Prefs.data_directory); | |
styleFile.append("elemhide.css"); | |
styleURL = Services.io.newFileURI(styleFile).QueryInterface(Ci.nsIFileURL); | |
}, | |
+ observe: function (subject, topic, data, additional) | |
+ { | |
+ if (topic != "content-document-global-created") | |
+ return; | |
+ | |
+ if (!Prefs.enabled || Policy.shouldNeverBlockWindow(subject)) | |
+ return; | |
+ | |
+ if (styleSheet) | |
+ { | |
+ try | |
+ { | |
+ let utils = subject.QueryInterface(Ci.nsIInterfaceRequestor) | |
+ .getInterface(Ci.nsIDOMWindowUtils); | |
+ utils.addSheet(styleSheet, Ci.nsIStyleSheetService.USER_SHEET); | |
+ } | |
+ catch (e) | |
+ { | |
+ Cu.reportError(e); | |
+ } | |
+ } | |
+ }, | |
+ | |
/** | |
* Removes all known filters | |
*/ | |
clear: function() | |
{ | |
filterByKey = Object.create(null); | |
keyByFilter = Object.create(null); | |
knownExceptions = Object.create(null); | |
@@ -214,17 +262,21 @@ let ElemHide = exports.ElemHide = | |
if (!ElemHide.isDirty || !Prefs.enabled) | |
{ | |
// Nothing changed, looks like we merely got enabled/disabled | |
if (Prefs.enabled && !ElemHide.applied) | |
{ | |
try | |
{ | |
- Utils.styleService.loadAndRegisterSheet(styleURL, Ci.nsIStyleSheetService.USER_SHEET); | |
+ if (!usePreloading) | |
+ Utils.styleService.loadAndRegisterSheet(styleURL, Ci.nsIStyleSheetService.USER_SHEET); | |
+ else | |
+ styleSheet = Utils.styleService.preloadSheet(styleURL, Ci.nsIStyleSheetService.USER_SHEET); | |
+ | |
ElemHide.applied = true; | |
} | |
catch (e) | |
{ | |
Cu.reportError(e); | |
} | |
} | |
else if (!Prefs.enabled && ElemHide.applied) | |
@@ -339,36 +391,28 @@ let ElemHide = exports.ElemHide = | |
* Unapplies current stylesheet URL | |
*/ | |
unapply: function() | |
{ | |
if (ElemHide.applied) | |
{ | |
try | |
{ | |
- Utils.styleService.unregisterSheet(styleURL, Ci.nsIStyleSheetService.USER_SHEET); | |
+ if (!usePreloading) | |
+ Utils.styleService.unregisterSheet(styleURL, Ci.nsIStyleSheetService.USER_SHEET); | |
} | |
catch (e) | |
{ | |
Cu.reportError(e); | |
} | |
ElemHide.applied = false; | |
} | |
}, | |
/** | |
- * Retrieves the currently applied stylesheet URL | |
- * @type String | |
- */ | |
- get styleURL() | |
- { | |
- return ElemHide.applied ? styleURL.spec : null; | |
- }, | |
- | |
- /** | |
* Retrieves an element hiding filter by the corresponding protocol key | |
*/ | |
getFilterByKey: function(/**String*/ key) /**Filter*/ | |
{ | |
return (key in filterByKey ? filterByKey[key] : null); | |
}, | |
/** |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# HG changeset patch | |
# Parent f3da596345f4c3c1e0906ddb3eb1a3d99a8dd3fa | |
diff --git a/lib/elemHide.js b/lib/elemHide.js | |
--- a/lib/elemHide.js | |
+++ b/lib/elemHide.js | |
@@ -412,17 +412,21 @@ let ElemHide = exports.ElemHide = | |
{ | |
// Only allow unqualified rules on a few protocols to prevent them from blocking chrome | |
yield '@-moz-document url-prefix("http://"),url-prefix("https://"),' | |
+ 'url-prefix("mailbox://"),url-prefix("imap://"),' | |
+ 'url-prefix("news://"),url-prefix("snews://"){'; | |
} | |
for (let selector in list) | |
- yield selector.replace(/[^\x01-\x7F]/g, escapeChar) + "{" + cssTemplate.replace("%ID%", list[selector]) + "}"; | |
+ yield selector.replace(/[^\x01-\x7F]/g, escapeChar) + "," | |
+ | |
+ yield ".dummmydummydummy" | |
+ yield "{ display: none; }"; | |
+ | |
yield '}'; | |
} | |
}, | |
/** | |
* Unapplies current stylesheet URL | |
*/ | |
unapply: function() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# HG changeset patch | |
# Parent cc45a3b6e343740bcc34288878562f2c2d7793ea | |
diff --git a/lib/elemHide.js b/lib/elemHide.js | |
--- a/lib/elemHide.js | |
+++ b/lib/elemHide.js | |
@@ -124,16 +124,48 @@ let ElemHide = exports.ElemHide = | |
observe: function (subject, topic, data, additional) | |
{ | |
if (topic != "content-document-global-created") | |
return; | |
if (!Prefs.enabled || Policy.shouldNeverBlockWindow(subject)) | |
return; | |
+ let domain = null; | |
+ let filters = null; | |
+ try | |
+ { | |
+ domain = subject.document.documentURIObject.host; | |
+ if (domain) | |
+ filters = ElemHide.getFiltersWithKeyForDomain(domain, true); | |
+ } catch (e) {} | |
+ | |
+ if (filters) | |
+ { | |
+ let list = Object.create(null); | |
+ for (let {key, filter} of filters) | |
+ list[key] = filter; | |
+ | |
+ let css = ""; | |
+ for (let line in this._generateCSSContent(list, false)) | |
+ css += line + "\n"; | |
+ | |
+ let uri = Services.io.newURI("data:text/css;base64," + btoa(css), null, null); | |
+ try | |
+ { | |
+ let utils = subject.QueryInterface(Ci.nsIInterfaceRequestor) | |
+ .getInterface(Ci.nsIDOMWindowUtils); | |
+ utils.loadSheet(uri, Ci.nsIStyleSheetService.USER_SHEET); | |
+ } | |
+ catch (e) | |
+ { | |
+ Cu.reportError(e); | |
+ } | |
+ } | |
+ | |
if (styleSheet) | |
{ | |
try | |
{ | |
let utils = subject.QueryInterface(Ci.nsIInterfaceRequestor) | |
.getInterface(Ci.nsIDOMWindowUtils); | |
utils.addSheet(styleSheet, Ci.nsIStyleSheetService.USER_SHEET); | |
} | |
@@ -282,17 +314,17 @@ let ElemHide = exports.ElemHide = | |
else if (!Prefs.enabled && ElemHide.applied) | |
{ | |
ElemHide.unapply(); | |
} | |
return; | |
} | |
- IO.writeToFile(styleURL.file, this._generateCSSContent(), function(e) | |
+ IO.writeToFile(styleURL.file, this._generateCSSContent(filterByKey, usePreloading), function(e) | |
{ | |
this._applying = false; | |
// _generateCSSContent is throwing NS_ERROR_NOT_AVAILABLE to indicate that | |
// there are no filters. If that exception is passed through XPCOM we will | |
// see a proper exception here, otherwise a number. | |
let noFilters = (e == Cr.NS_ERROR_NOT_AVAILABLE || (e && e.result == Cr.NS_ERROR_NOT_AVAILABLE)); | |
if (noFilters) | |
@@ -329,26 +361,29 @@ let ElemHide = exports.ElemHide = | |
FilterNotifier.triggerListeners("elemhideupdate"); | |
} | |
}.bind(this)); | |
this._applying = true; | |
}, | |
- _generateCSSContent: function() | |
+ _generateCSSContent: function(filters, onlyGlobal) | |
{ | |
// Grouping selectors by domains | |
let domains = Object.create(null); | |
let hasFilters = false; | |
- for (let key in filterByKey) | |
+ for (let key in filters) | |
{ | |
- let filter = filterByKey[key]; | |
+ let filter = filters[key]; | |
let domain = filter.selectorDomain || ""; | |
+ if (onlyGlobal && domain) | |
+ continue; | |
+ | |
let list; | |
if (domain in domains) | |
list = domains[domain]; | |
else | |
{ | |
list = Object.create(null); | |
domains[domain] = list; | |
} | |
@@ -427,10 +462,27 @@ let ElemHide = exports.ElemHide = | |
let filter = filterByKey[key]; | |
if (specificOnly && (!filter.domains || filter.domains[""])) | |
continue; | |
if (filter.isActiveOnDomain(domain) && !this.getException(filter, domain)) | |
result.push(filter.selector); | |
} | |
return result; | |
+ }, | |
+ | |
+ getFiltersWithKeyForDomain: function(/**String*/ domain) | |
+ { | |
+ let result = []; | |
+ for (let key in filterByKey) | |
+ { | |
+ let filter = filterByKey[key]; | |
+ | |
+ if (!filter.domains) | |
+ continue | |
+ | |
+ if (filter.isActiveOnDomain(domain) && !this.getException(filter, domain)) | |
+ result.push({key, filter}); | |
+ } | |
+ return result; | |
} | |
+ | |
}; | |
diff --git a/lib/elemHideHitRegistration.js b/lib/elemHideHitRegistration.js | |
--- a/lib/elemHideHitRegistration.js | |
+++ b/lib/elemHideHitRegistration.js | |
@@ -134,16 +134,18 @@ HitRegistrationChannel.prototype = { | |
let filter = ElemHide.getFilterByKey(this.key); | |
if (filter) | |
{ | |
let wnd = Utils.getRequestWindow(this); | |
if (wnd && wnd.document && !Policy.processNode(wnd, wnd.document, Policy.type.ELEMHIDE, filter)) | |
data = "<bindings xmlns='http://www.mozilla.org/xbl'/>"; | |
} | |
+ dump("filter: " + filter + " data: " + data + "\n"); | |
+ | |
let stream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(Ci.nsIStringInputStream); | |
stream.setData(data, data.length); | |
return stream; | |
}, | |
isPending: function() | |
{ | |
return false; | |
}, |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment