Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: lib/snippets.js

Issue 29737561: Issue 6539, 6782 - Implement support for snippets (Closed) Base URL: https://hg.adblockplus.org/adblockpluschrome/
Patch Set: Check filter against current frame, polish up Created April 3, 2018, 10:41 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « lib/requestBlocker.js ('k') | metadata.chrome » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 * This file is part of Adblock Plus <https://adblockplus.org/>,
3 * Copyright (C) 2006-present eyeo GmbH
4 *
5 * Adblock Plus is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation.
8 *
9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 "use strict";
19
20 const {defaultMatcher} = require("matcher");
21 const {RegExpFilter, WhitelistFilter} = require("filterClasses");
22 const {verifySignature} = require("rsa");
23 const {extractHostFromFrame, isThirdParty, stringifyURL} = require("url");
24 const {checkWhitelisted} = require("whitelisting");
25 const {FilterNotifier} = require("filterNotifier");
26 const devtools = require("devtools");
27 const info = require("info");
28
29 const {typeMap} = RegExpFilter;
30
31 const remoteURL = "https://easylist-downloads.adblockplus.org/snippets.js";
32
33 let libraries = {local: "", remote: ""};
34 let executableCode = new Map();
35
36 function fetchText(url)
37 {
38 return fetch(url, {cache: "no-cache"}).then(
39 response => response.ok ? response.text() : ""
40 );
41 }
42
43 function updateLibrary(name, text)
44 {
45 libraries[name] = text;
46
47 executableCode.clear();
48 }
49
50 function checkLibrarySignature(url, text)
51 {
52 return fetchText(`${url}.sig`).then(
53 signature => fetchText(browser.extension.getURL("/key")).then(
54 key => verifySignature(key.replace(/=/g, ""), signature, text)
55 )
56 );
57 }
58
59 function loadLibrary(name, url, {verify = true} = {})
60 {
61 fetchText(url).then(text =>
62 {
63 if (text != libraries[name])
64 {
65 let check = verify ? checkLibrarySignature(url, text) :
66 Promise.resolve(true);
67 check.then(ok =>
68 {
69 if (ok)
70 updateLibrary(name, text);
71 });
72 }
73 });
74 }
75
76 function loadLocalLibrary()
77 {
78 loadLibrary("local", browser.extension.getURL("/snippets.js"),
79 {verify: false});
80 }
81
82 function loadRemoteLibrary()
83 {
84 loadLibrary("remote", remoteURL);
85 }
86
87 function getExecutableCode(snippet)
88 {
89 let code = executableCode.get(snippet);
90 if (code)
91 return code;
92
93 code = `
94 "use strict";
95 {
96 let localImports = Object.create(null);
97 let remoteImports = Object.create(null);
98 new Function("exports", ${JSON.stringify(libraries.local)})(
99 localImports
100 );
101 new Function("exports", ${JSON.stringify(libraries.remote)})(
102 remoteImports
103 );
104 let imports = Object.assign(Object.create(null),
105 localImports,
106 remoteImports);
107 let key = ${JSON.stringify(snippet)};
108 if (Object.prototype.hasOwnProperty.call(imports, key))
109 {
110 let value = imports[key];
111 if (typeof value == "function")
112 value();
113 }
114 }
115 `;
116
117 executableCode.set(snippet, code);
118 return code;
119 }
120
121 function injectCode(snippet, tabId, frameId)
122 {
123 try
124 {
125 browser.tabs.executeScript(tabId, {
126 code: getExecutableCode(snippet),
127 frameId,
128 matchAboutBlank: true,
129 runAt: "document_start"
130 });
131 }
132 catch (error)
133 {
134 }
135 }
136
137 loadLocalLibrary();
138
139 // Only Chrome supports dynamic loading of JS.
140 if (info.platform == "chromium")
141 {
142 loadRemoteLibrary();
143
144 // Download every 24 hours.
145 setInterval(loadRemoteLibrary, 24 * 60 * 60 * 1000);
146 }
147
148 browser.webNavigation.onCommitted.addListener(({tabId, frameId, url}) =>
149 {
150 // There's a bug in Chrome that causes webNavigation.onCommitted to get
151 // dispatched twice if there's a URL filter present, therefore we must listen
152 // for all URLs and do an explicit check here.
153 // https://crbug.com/827855
154 if (!/^(https?:\/\/|about:blank\b|about:srcdoc\b)/.test(url))
155 return;
156
157 let urlObject = new URL(url);
158 let urlString = stringifyURL(urlObject);
159 let frame = ext.getFrame(tabId, frameId);
160 let hostname = extractHostFromFrame(frame);
161 let thirdParty = isThirdParty(urlObject, hostname);
162
163 let filter = defaultMatcher.matchesAny(urlString, typeMap.SNIPPET, hostname,
164 thirdParty, null, true);
165 if (!filter)
166 return;
167
168 let page = new ext.Page({id: tabId, url});
169
170 if (checkWhitelisted(page, frame))
171 return;
172
173 devtools.logRequest(page, urlString, "SNIPPET", hostname, thirdParty, null,
174 true, filter);
175 FilterNotifier.emit("filter.hitCount", filter, 0, 0, page);
176
177 if (filter instanceof WhitelistFilter)
178 return;
179
180 for (let snippet of filter.snippets)
181 injectCode(snippet, tabId, frameId);
182 });
OLDNEW
« no previous file with comments | « lib/requestBlocker.js ('k') | metadata.chrome » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld