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

Side by Side Diff: polyfill.js

Issue 29585594: Issue 4579 - Wrap runtime.onMessage (Closed) Base URL: https://hg.adblockplus.org/adblockpluschrome/
Patch Set: Rebase Created Jan. 9, 2018, 9:31 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 | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
46 ]; 46 ];
47 47
48 // 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,
49 // 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,
50 // it sets runtime.lastError 50 // it sets runtime.lastError
51 const portClosedBeforeResponseError = 51 const portClosedBeforeResponseError =
52 // Older versions of Chrome have a typo: 52 // Older versions of Chrome have a typo:
53 // https://crrev.com/c33f51726eacdcc1a487b21a13611f7eab580d6d 53 // https://crrev.com/c33f51726eacdcc1a487b21a13611f7eab580d6d
54 /^The message port closed before a res?ponse was received\.$/; 54 /^The message port closed before a res?ponse was received\.$/;
55 55
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
56 function wrapAPI(api) 62 function wrapAPI(api)
57 { 63 {
58 let object = browser; 64 let object = browser;
59 let path = api.split("."); 65 let path = api.split(".");
60 let name = path.pop(); 66 let name = path.pop();
61 67
62 for (let node of path) 68 for (let node of path)
63 { 69 {
64 object = object[node]; 70 object = object[node];
65 71
66 if (!object) 72 if (!object)
67 return; 73 return;
68 } 74 }
69 75
70 let func = object[name]; 76 let func = object[name];
71 if (!func) 77 if (!func)
72 return; 78 return;
79
73 let descriptor = Object.getOwnPropertyDescriptor(object, name); 80 let descriptor = Object.getOwnPropertyDescriptor(object, name);
81
74 delete descriptor["get"]; 82 delete descriptor["get"];
75 delete descriptor["set"]; 83 delete descriptor["set"];
84
76 descriptor.value = function(...args) 85 descriptor.value = function(...args)
77 { 86 {
78 let callStack = new Error().stack; 87 let callStack = new Error().stack;
79 88
80 if (typeof args[args.length - 1] == "function") 89 if (typeof args[args.length - 1] == "function")
81 return func.apply(object, args); 90 return func.apply(object, args);
82 91
83 // 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
84 // 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
85 // 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
(...skipping 21 matching lines...) Expand all
107 116
108 reject(error); 117 reject(error);
109 } 118 }
110 else 119 else
111 { 120 {
112 resolve(result); 121 resolve(result);
113 } 122 }
114 }); 123 });
115 }); 124 });
116 }; 125 };
126
117 Object.defineProperty(object, name, descriptor); 127 Object.defineProperty(object, name, descriptor);
118 } 128 }
119 129
130 function wrapMessageAPIs()
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 };
192 }
193
120 function shouldWrapAPIs() 194 function shouldWrapAPIs()
121 { 195 {
122 try 196 try
123 { 197 {
124 return !(browser.storage.local.get([]) instanceof Promise); 198 return !(browser.storage.local.get([]) instanceof Promise);
125 } 199 }
126 catch (error) 200 catch (error)
127 { 201 {
128 } 202 }
129 203
130 return true; 204 return true;
131 } 205 }
132 206
133 if (shouldWrapAPIs()) 207 if (shouldWrapAPIs())
134 { 208 {
135 // Unlike Firefox and Microsoft Edge, Chrome doesn't have a "browser" 209 // Unlike Firefox and Microsoft Edge, Chrome doesn't have a "browser"
136 // object, but provides the extension API through the "chrome" namespace 210 // object, but provides the extension API through the "chrome" namespace
137 // (non-standard). 211 // (non-standard).
138 if (typeof browser == "undefined") 212 if (typeof browser == "undefined")
139 window.browser = chrome; 213 window.browser = chrome;
140 214
141 for (let api of asyncAPIs) 215 for (let api of asyncAPIs)
142 wrapAPI(api); 216 wrapAPI(api);
217
218 wrapMessageAPIs();
143 } 219 }
144 220
145 // Workaround since HTMLCollection, NodeList, StyleSheetList, and CSSRuleList 221 // Workaround since HTMLCollection, NodeList, StyleSheetList, and CSSRuleList
146 // didn't have iterator support before Chrome 51. 222 // didn't have iterator support before Chrome 51.
147 // https://bugs.chromium.org/p/chromium/issues/detail?id=401699 223 // https://bugs.chromium.org/p/chromium/issues/detail?id=401699
148 for (let object of [HTMLCollection, NodeList, StyleSheetList, CSSRuleList]) 224 for (let object of [HTMLCollection, NodeList, StyleSheetList, CSSRuleList])
149 { 225 {
150 if (!(Symbol.iterator in object.prototype)) 226 if (!(Symbol.iterator in object.prototype))
151 object.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; 227 object.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
152 } 228 }
153 } 229 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld