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

Delta Between Two Patch Sets: polyfill.js

Issue 29585594: Issue 4579 - Wrap runtime.onMessage (Closed) Base URL: https://hg.adblockplus.org/adblockpluschrome/
Left Patch Set: Created Oct. 23, 2017, 2:25 a.m.
Right Patch Set: Rename functions Created Jan. 9, 2018, 9:36 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « no previous file | no next file » | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 /* 1 /*
2 * This file is part of Adblock Plus <https://adblockplus.org/>, 2 * This file is part of Adblock Plus <https://adblockplus.org/>,
3 * Copyright (C) 2006-present eyeo GmbH 3 * Copyright (C) 2006-present eyeo GmbH
4 * 4 *
5 * Adblock Plus is free software: you can redistribute it and/or modify 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 6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation. 7 * published by the Free Software Foundation.
8 * 8 *
9 * Adblock Plus is distributed in the hope that it will be useful, 9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details. 12 * GNU General Public License for more details.
13 * 13 *
14 * You should have received a copy of the GNU General Public License 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/>. 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
16 */ 16 */
17 17
18 "use strict"; 18 "use strict";
19 19
20 { 20 {
21 const asyncAPIs = [ 21 const asyncAPIs = [
22 "contextMenus.removeAll", 22 "contextMenus.removeAll",
23 "devtools.panels.create", 23 "devtools.panels.create",
24 "notifications.clear", 24 "notifications.clear",
25 "notifications.create", 25 "notifications.create",
26 "runtime.getBrowserInfo",
26 "runtime.openOptionsPage", 27 "runtime.openOptionsPage",
27 "runtime.sendMessage", 28 "runtime.sendMessage",
28 "runtime.setUninstallURL", 29 "runtime.setUninstallURL",
29 "storage.local.get", 30 "storage.local.get",
30 "storage.local.remove", 31 "storage.local.remove",
31 "storage.local.set", 32 "storage.local.set",
32 "storage.managed.get", 33 "storage.managed.get",
33 "tabs.create", 34 "tabs.create",
34 "tabs.get", 35 "tabs.get",
35 "tabs.getCurrent", 36 "tabs.getCurrent",
36 "tabs.insertCSS", 37 "tabs.insertCSS",
37 "tabs.query", 38 "tabs.query",
38 "tabs.reload", 39 "tabs.reload",
39 "tabs.sendMessage", 40 "tabs.sendMessage",
40 "tabs.update", 41 "tabs.update",
41 "webNavigation.getAllFrames", 42 "webNavigation.getAllFrames",
42 "webRequest.handlerBehaviorChanged", 43 "webRequest.handlerBehaviorChanged",
43 "windows.create", 44 "windows.create",
44 "windows.update" 45 "windows.update"
45 ]; 46 ];
46 47
47 // Since we add a callback for all messaging API calls in our wrappers, 48 // Since we add a callback for all messaging API calls in our wrappers,
48 // Chrome assumes we're interested in the response; when there's no response, 49 // Chrome assumes we're interested in the response; when there's no response,
49 // it sets runtime.lastError 50 // it sets runtime.lastError
50 const portClosedBeforeResponseError = 51 const portClosedBeforeResponseError =
51 // Older versions of Chrome have a typo: 52 // Older versions of Chrome have a typo:
52 // https://crrev.com/c33f51726eacdcc1a487b21a13611f7eab580d6d 53 // https://crrev.com/c33f51726eacdcc1a487b21a13611f7eab580d6d
53 /^The message port closed before a res?ponse was received\.$/; 54 /^The message port closed before a res?ponse was received\.$/;
54 55
55 function wrapAPI(api) 56 // This is the error Firefox throws when a message listener is not a
57 // function.
58 const invalidMessageListenerError = "Invalid listener for runtime.onMessage.";
59
60 let messageListeners = new WeakMap();
61
62 function wrapAsyncAPI(api)
56 { 63 {
57 let object = browser; 64 let object = browser;
58 let path = api.split("."); 65 let path = api.split(".");
59 let name = path.pop(); 66 let name = path.pop();
60 67
61 for (let node of path) 68 for (let node of path)
62 { 69 {
63 object = object[node]; 70 object = object[node];
64 71
65 if (!object) 72 if (!object)
66 return; 73 return;
67 } 74 }
68 75
69 let func = object[name]; 76 let func = object[name];
70 object[name] = function(...args) 77 if (!func)
78 return;
79
80 let descriptor = Object.getOwnPropertyDescriptor(object, name);
81
82 delete descriptor["get"];
83 delete descriptor["set"];
84
85 descriptor.value = function(...args)
71 { 86 {
72 let callStack = new Error().stack; 87 let callStack = new Error().stack;
73 88
74 if (typeof args[args.length - 1] == "function") 89 if (typeof args[args.length - 1] == "function")
75 return func.apply(object, args); 90 return func.apply(object, args);
76 91
77 // If the last argument is undefined, we drop it from the list assuming 92 // If the last argument is undefined, we drop it from the list assuming
78 // it stands for the optional callback. We must do this, because we have 93 // it stands for the optional callback. We must do this, because we have
79 // to replace it with our own callback. If we simply append our own 94 // to replace it with our own callback. If we simply append our own
80 // callback to the list, it won't match the signature of the function and 95 // callback to the list, it won't match the signature of the function and
(...skipping 20 matching lines...) Expand all
101 116
102 reject(error); 117 reject(error);
103 } 118 }
104 else 119 else
105 { 120 {
106 resolve(result); 121 resolve(result);
107 } 122 }
108 }); 123 });
109 }); 124 });
110 }; 125 };
126
127 Object.defineProperty(object, name, descriptor);
128 }
129
130 function wrapRuntimeOnMessage()
131 {
132 let {onMessage} = browser.runtime;
133 let {addListener, removeListener, hasListener} = onMessage;
134
135 onMessage.addListener = function(listener)
136 {
137 if (typeof listener != "function")
138 throw new Error(invalidMessageListenerError);
139
140 // Don't add the same listener twice or we end up with multiple wrappers.
141 if (messageListeners.has(listener))
142 return;
143
144 let wrapper = (message, sender, sendResponse) =>
145 {
146 let wait = listener(message, sender, sendResponse);
147
148 if (wait instanceof Promise)
149 {
150 wait.then(sendResponse, reason =>
151 {
152 try
153 {
154 sendResponse();
155 }
156 finally
157 {
158 // sendResponse can throw if the internal port is closed; be sure
159 // to throw the original error.
160 throw reason;
161 }
162 });
163 }
164
165 return !!wait;
166 };
167
168 addListener.call(onMessage, wrapper);
169 messageListeners.set(listener, wrapper);
170 };
171
172 onMessage.removeListener = function(listener)
173 {
174 if (typeof listener != "function")
175 throw new Error(invalidMessageListenerError);
176
177 let wrapper = messageListeners.get(listener);
178 if (wrapper)
179 {
180 removeListener.call(onMessage, wrapper);
181 messageListeners.delete(listener);
182 }
183 };
184
185 onMessage.hasListener = function(listener)
186 {
187 if (typeof listener != "function")
188 throw new Error(invalidMessageListenerError);
189
190 return messageListeners.has(listener);
191 };
111 } 192 }
112 193
113 function shouldWrapAPIs() 194 function shouldWrapAPIs()
114 { 195 {
115 try 196 try
116 { 197 {
117 return !(browser.storage.local.get([]) instanceof Promise); 198 return !(browser.storage.local.get([]) instanceof Promise);
118 } 199 }
119 catch (error) 200 catch (error)
120 { 201 {
121 } 202 }
122 203
123 return true; 204 return true;
124 } 205 }
125 206
126 if (shouldWrapAPIs()) 207 if (shouldWrapAPIs())
127 { 208 {
128 // Unlike Firefox and Microsoft Edge, Chrome doesn't have a "browser" 209 // Unlike Firefox and Microsoft Edge, Chrome doesn't have a "browser"
Manish Jethani 2017/10/23 02:32:45 Unrelated, but there's an ESLint error here becaus
129 // object, but provides the extension API through the "chrome" namespace 210 // object, but provides the extension API through the "chrome" namespace
130 // (non-standard). 211 // (non-standard).
131 if (typeof browser == "undefined") 212 if (typeof browser == "undefined")
132 window.browser = chrome; 213 window.browser = chrome;
133 214
134 for (let api of asyncAPIs) 215 for (let api of asyncAPIs)
135 wrapAPI(api); 216 wrapAsyncAPI(api);
136 217
137 let {addListener} = browser.runtime.onMessage; 218 wrapRuntimeOnMessage();
138 browser.runtime.onMessage.addListener = function(listener)
139 {
140 addListener.call(browser.runtime.onMessage, (
141 message, sender, sendResponse
142 ) =>
143 {
144 let wait = listener(message, sender, sendResponse);
145
146 if (wait instanceof Promise)
Manish Jethani 2017/10/23 02:32:45 There's no need to add a catch handler, Chrome (an
Wladimir Palant 2017/10/23 08:53:26 Not really, the message clearly says that not hand
Manish Jethani 2017/10/23 12:21:04 Did you mean that Chrome will no longer do this au
147 wait.then(sendResponse);
148
149 return !!wait;
150 });
151 };
Wladimir Palant 2017/10/23 08:53:26 You have a problem here, removeListener will no lo
Manish Jethani 2017/10/23 12:21:04 Done.
152 } 219 }
153 220
154 // Workaround since HTMLCollection, NodeList, StyleSheetList, and CSSRuleList 221 // Workaround since HTMLCollection, NodeList, StyleSheetList, and CSSRuleList
155 // didn't have iterator support before Chrome 51. 222 // didn't have iterator support before Chrome 51.
156 // https://bugs.chromium.org/p/chromium/issues/detail?id=401699 223 // https://bugs.chromium.org/p/chromium/issues/detail?id=401699
157 for (let object of [HTMLCollection, NodeList, StyleSheetList, CSSRuleList]) 224 for (let object of [HTMLCollection, NodeList, StyleSheetList, CSSRuleList])
158 { 225 {
159 if (!(Symbol.iterator in object.prototype)) 226 if (!(Symbol.iterator in object.prototype))
160 object.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; 227 object.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
161 } 228 }
162 } 229 }
LEFTRIGHT
« no previous file | no next file » | Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Toggle Comments ('s')

Powered by Google App Engine
This is Rietveld