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

Side by Side Diff: inject.preload.js

Issue 29437555: Issue 4586, 5207 - Move the injected wrappers into a separate file (Closed)
Patch Set: Avoid adding an extra content script Created May 12, 2017, 12:53 p.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 | « include.preload.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-2017 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 let randomEventName = "abp-request-" + Math.random().toString(36).substr(2);
21
22 // Proxy "should we block?" messages from checkRequest inside the injected
23 // code to the background page and back again.
24 document.addEventListener(randomEventName, event =>
25 {
26 let {url, requestType} = event.detail;
27
28 ext.backgroundPage.sendMessage({
29 type: "request.blockedByWrapper",
30 requestType,
31 url
32 }, block =>
33 {
34 document.dispatchEvent(new CustomEvent(
35 randomEventName + "-" + requestType + "-" + url, {detail: block}
36 ));
37 });
38 });
39
40 let injected = function(eventName)
41 {
42 /*
43 * Shadow root getter wrapper
44 *
45 * After creating our shadowRoot we must wrap the getter to prevent the
46 * website from accessing it (#4191, #4298). This is required as a
47 * workaround for the lack of user style support in Chrome.
48 * See https://bugs.chromium.org/p/chromium/issues/detail?id=632009&desc=2
49 */
50 if ("shadowRoot" in Element.prototype)
51 {
52 let ourShadowRoot = document.documentElement.shadowRoot;
53 if (ourShadowRoot)
54 {
55 let desc = Object.getOwnPropertyDescriptor(Element.prototype,
56 "shadowRoot");
57 let shadowRoot = Function.prototype.call.bind(desc.get);
58
59 Object.defineProperty(Element.prototype, "shadowRoot", {
60 configurable: true, enumerable: true, get()
61 {
62 let thisShadow = shadowRoot(this);
63 return thisShadow == ourShadowRoot ? null : thisShadow;
64 }
65 });
66 }
67 }
68
69 /*
70 * Shared request checking code, used by both the WebSocket and
71 * RTCPeerConnection wrappers.
72 */
73 let RealCustomEvent = window.CustomEvent;
74 let addEventListener = document.addEventListener.bind(document);
75 let removeEventListener = document.removeEventListener.bind(document);
76 let dispatchEvent = document.dispatchEvent.bind(document);
77
78 function checkRequest(requestType, url, callback)
79 {
80 let incomingEventName = eventName + "-" + requestType + "-" + url;
81
82 function listener(event)
83 {
84 callback(event.detail);
85 removeEventListener(incomingEventName, listener);
86 }
87 addEventListener(incomingEventName, listener);
88
89 dispatchEvent(new RealCustomEvent(eventName, {detail: {url, requestType}}));
90 }
91
92 // Only to be called before the page's code, not hardened.
93 function copyProperties(src, dest, properties)
94 {
95 for (let name of properties)
96 {
97 Object.defineProperty(dest, name,
98 Object.getOwnPropertyDescriptor(src, name));
99 }
100 }
101
102 /*
103 * WebSocket wrapper
104 *
105 * Required before Chrome 58, since the webRequest API didn't allow us to
106 * intercept WebSockets.
107 * See https://bugs.chromium.org/p/chromium/issues/detail?id=129353
108 */
109 let RealWebSocket = WebSocket;
110 let closeWebSocket = Function.prototype.call.bind(
111 RealWebSocket.prototype.close
112 );
113
114 function WrappedWebSocket(url, ...args)
115 {
116 // Throw correct exceptions if the constructor is used improperly.
117 if (!(this instanceof WrappedWebSocket)) return RealWebSocket();
118 if (arguments.length < 1) return new RealWebSocket();
119
120 let websocket = new RealWebSocket(url, ...args);
121
122 checkRequest("WEBSOCKET", websocket.url, blocked =>
123 {
124 if (blocked)
125 closeWebSocket(websocket);
126 });
127
128 return websocket;
129 }
130 WrappedWebSocket.prototype = RealWebSocket.prototype;
131 window.WebSocket = WrappedWebSocket.bind();
132 copyProperties(RealWebSocket, WebSocket,
133 ["CONNECTING", "OPEN", "CLOSING", "CLOSED", "prototype"]);
134 RealWebSocket.prototype.constructor = WebSocket;
135
136 /*
137 * RTCPeerConnection wrapper
138 *
139 * The webRequest API in Chrome does not yet allow the blocking of
140 * WebRTC connections.
141 * See https://bugs.chromium.org/p/chromium/issues/detail?id=707683
142 */
143 let RealRTCPeerConnection = window.RTCPeerConnection ||
144 window.webkitRTCPeerConnection;
145 let closeRTCPeerConnection = Function.prototype.call.bind(
146 RealRTCPeerConnection.prototype.close
147 );
148 let RealArray = Array;
149 let RealString = String;
150 let {create: createObject, defineProperty} = Object;
151
152 function normalizeUrl(url)
153 {
154 if (typeof url != "undefined")
155 return RealString(url);
156 }
157
158 function safeCopyArray(originalArray, transform)
159 {
160 if (originalArray == null || typeof originalArray != "object")
161 return originalArray;
162
163 let safeArray = RealArray(originalArray.length);
164 for (let i = 0; i < safeArray.length; i++)
165 {
166 defineProperty(safeArray, i, {
167 configurable: false, enumerable: false, writable: false,
168 value: transform(originalArray[i])
169 });
170 }
171 defineProperty(safeArray, "length", {
172 configurable: false, enumerable: false, writable: false,
173 value: safeArray.length
174 });
175 return safeArray;
176 }
177
178 // It would be much easier to use the .getConfiguration method to obtain
179 // the normalized and safe configuration from the RTCPeerConnection
180 // instance. Unfortunately its not implemented as of Chrome unstable 59.
181 // See https://www.chromestatus.com/feature/5271355306016768
182 function protectConfiguration(configuration)
183 {
184 if (configuration == null || typeof configuration != "object")
185 return configuration;
186
187 let iceServers = safeCopyArray(
188 configuration.iceServers,
189 iceServer =>
190 {
191 let {url, urls} = iceServer;
192
193 // RTCPeerConnection doesn't iterate through pseudo Arrays of urls.
194 if (typeof urls != "undefined" && !(urls instanceof RealArray))
195 urls = [urls];
196
197 return createObject(iceServer, {
198 url: {
199 configurable: false, enumerable: false, writable: false,
200 value: normalizeUrl(url)
201 },
202 urls: {
203 configurable: false, enumerable: false, writable: false,
204 value: safeCopyArray(urls, normalizeUrl)
205 }
206 });
207 }
208 );
209
210 return createObject(configuration, {
211 iceServers: {
212 configurable: false, enumerable: false, writable: false,
213 value: iceServers
214 }
215 });
216 }
217
218 function checkUrl(peerconnection, url)
219 {
220 checkRequest("WEBRTC", url, blocked =>
221 {
222 if (blocked)
223 {
224 // Calling .close() throws if already closed.
225 try
226 {
227 closeRTCPeerConnection(peerconnection);
228 }
229 catch (e) {}
230 }
231 });
232 }
233
234 function checkConfiguration(peerconnection, configuration)
235 {
236 if (configuration && configuration.iceServers)
237 {
238 for (let i = 0; i < configuration.iceServers.length; i++)
239 {
240 let iceServer = configuration.iceServers[i];
241 if (iceServer)
242 {
243 if (iceServer.url)
244 checkUrl(peerconnection, iceServer.url);
245
246 if (iceServer.urls)
247 {
248 for (let j = 0; j < iceServer.urls.length; j++)
249 checkUrl(peerconnection, iceServer.urls[j]);
250 }
251 }
252 }
253 }
254 }
255
256 // Chrome unstable (tested with 59) has already implemented
257 // setConfiguration, so we need to wrap that if it exists too.
258 // https://www.chromestatus.com/feature/5596193748942848
259 if (RealRTCPeerConnection.prototype.setConfiguration)
260 {
261 let realSetConfiguration = Function.prototype.call.bind(
262 RealRTCPeerConnection.prototype.setConfiguration
263 );
264
265 RealRTCPeerConnection.prototype.setConfiguration = function(configuration)
266 {
267 configuration = protectConfiguration(configuration);
268
269 // Call the real method first, so that validates the configuration for
270 // us. Also we might as well since checkRequest is asynchronous anyway.
271 realSetConfiguration(this, configuration);
272 checkConfiguration(this, configuration);
273 };
274 }
275
276 function WrappedRTCPeerConnection(...args)
277 {
278 if (!(this instanceof WrappedRTCPeerConnection))
279 return WrappedRTCPeerConnection();
280
281 let configuration = protectConfiguration(args[0]);
282 // Since the old webkitRTCPeerConnection constructor takes an optional
283 // second argument we need to take care to pass that through. Necessary
284 // for older versions of Chrome such as 49.
285 let peerconnection = new RealRTCPeerConnection(configuration, args[1]);
286 checkConfiguration(peerconnection, configuration);
287 return peerconnection;
288 }
289
290 WrappedRTCPeerConnection.prototype = RealRTCPeerConnection.prototype;
291
292 let boundWrappedRTCPeerConnection = WrappedRTCPeerConnection.bind();
293 copyProperties(RealRTCPeerConnection, boundWrappedRTCPeerConnection,
294 ["caller", "generateCertificate", "name", "prototype"]);
295 RealRTCPeerConnection.prototype.constructor = boundWrappedRTCPeerConnection;
296
297 if ("RTCPeerConnection" in window)
298 window.RTCPeerConnection = boundWrappedRTCPeerConnection;
299 if ("webkitRTCPeerConnection" in window)
300 window.webkitRTCPeerConnection = boundWrappedRTCPeerConnection;
301 };
302
303 if (document instanceof HTMLDocument)
304 {
305 let script = document.createElement("script");
306 script.type = "application/javascript";
307 script.async = false;
308 script.textContent = "(" + injected + ")('" + randomEventName + "');";
309 document.documentElement.appendChild(script);
310 document.documentElement.removeChild(script);
311 }
OLDNEW
« no previous file with comments | « include.preload.js ('k') | metadata.chrome » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld