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

Side by Side Diff: safari/contentBlocking.js

Issue 29503587: Issue 5464 - Upgrade to new asynchronous version of abp2blocklist (Closed) Base URL: https://hg.adblockplus.org/adblockpluschrome/
Patch Set: Split out setContentBlocker handlers into then and catch, move patch to lib/requestBlocker.js Created Aug. 16, 2017, 10:17 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') | 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-2017 eyeo GmbH 3 * Copyright (C) 2006-2017 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 19 matching lines...) Expand all
30 { 30 {
31 function onLegacyAPISupported(msg, sender) 31 function onLegacyAPISupported(msg, sender)
32 { 32 {
33 port.off("safari.legacyAPISupported", onLegacyAPISupported); 33 port.off("safari.legacyAPISupported", onLegacyAPISupported);
34 resolve(msg.legacyAPISupported); 34 resolve(msg.legacyAPISupported);
35 } 35 }
36 port.on("safari.legacyAPISupported", onLegacyAPISupported); 36 port.on("safari.legacyAPISupported", onLegacyAPISupported);
37 }); 37 });
38 let contentBlockingActive = false; 38 let contentBlockingActive = false;
39 let afterContentBlockingFinished = null; 39 let afterContentBlockingFinished = null;
40 let pendingContentBlockerUpdate = null;
40 let contentBlockListDirty = true; 41 let contentBlockListDirty = true;
41 let lastSetContentBlockerError; 42 let lastSetContentBlockerError;
42 43
43 function clearBlockCounters() 44 function clearBlockCounters()
44 { 45 {
45 ext.pages.query({}, pages => 46 ext.pages.query({}, pages =>
46 { 47 {
47 for (let page of pages) 48 for (let page of pages)
48 page.browserAction.setBadge(); 49 page.browserAction.setBadge();
49 }); 50 });
50 } 51 }
51 52
52 function setContentBlocker(callback) 53 function setContentBlocker()
53 { 54 {
54 // When given the same rules as last time setContentBlocker will always give 55 return new Promise((resolve, reject) =>
55 // null (success) to the callback, even when there was actually an error. We
56 // cache the last result therefore so that we can provide a consistent result
57 // and also to avoid wastefully regenerating an identical blocklist.
58 if (!contentBlockListDirty)
59 { 56 {
60 callback(lastSetContentBlockerError); 57 // Reset state and either fulfill or reject this promise.
61 return; 58 let completePromise = error =>
62 } 59 {
60 lastSetContentBlockerError = error;
61 contentBlockListDirty = false;
63 62
64 let contentBlockerList = new ContentBlockerList(); 63 if (error)
65 for (let subscription of FilterStorage.subscriptions) 64 reject(error);
66 if (!subscription.disabled) 65 else
67 for (let filter of subscription.filters) 66 resolve();
68 contentBlockerList.addFilter(filter); 67 };
69 68
70 contentBlockListDirty = false; 69 // When given the same rules as last time setContentBlocker will always
71 safari.extension.setContentBlocker( 70 // resolve with null (success), even when there was actually an
72 // There is a strange bug in setContentBlocker for Safari 9 where if both 71 // error. We cache the last result therefore so that we can provide a
73 // the callback parameter is provided and the rules aren't converted to a 72 // consistent result and also to avoid wastefully regenerating an identical
74 // JSON string it fails. Worse still it actually performs the callback twice 73 // blocklist.
75 // too, firstly with an empty string and then with an Error: 74 if (!contentBlockListDirty)
76 // "Extension compilation failed: Failed to parse the JSON String."
77 // To mitigate this we convert the rules to JSON here and also ignore
78 // callback values of "". (Usually the callback is performed with either
79 // null for success or an Error on failure.)
80 // Bug #26322821 filed on bugreport.apple.com
81 JSON.stringify(contentBlockerList.generateRules()),
82 function(error)
83 { 75 {
84 if (error == "") 76 completePromise(lastSetContentBlockerError);
85 return; 77 return;
78 }
86 79
87 lastSetContentBlockerError = error; 80 let contentBlockerList = new ContentBlockerList();
88 callback(error); 81 for (let subscription of FilterStorage.subscriptions)
82 {
83 if (!subscription.disabled)
84 {
85 for (let filter of subscription.filters)
86 contentBlockerList.addFilter(filter);
87 }
89 } 88 }
90 ); 89
90 contentBlockerList.generateRules().then(rules =>
91 {
92 safari.extension.setContentBlocker(
93 // There is a strange bug in setContentBlocker for Safari 9 where if
94 // both the callback parameter is provided and the rules aren't
95 // converted to a JSON string it fails. Worse still it actually
96 // performs the callback twice too, firstly with an empty string and
97 // then with an Error: "Extension compilation failed: Failed to parse
98 // the JSON String." To mitigate this we convert the rules to JSON here
99 // and also ignore callback values of "". (Usually the callback is
100 // performed with either null for success or an Error on failure.)
101 // Bug #26322821 filed on bugreport.apple.com
102 JSON.stringify(rules),
103 function(error)
104 {
105 if (error == "")
106 return;
107
108 completePromise(error);
109 }
110 );
111 })
112 .catch(completePromise);
113 });
91 } 114 }
92 115
93 function updateContentBlocker(isStartup, legacyAPISupported) 116 function updateContentBlocker(isStartup, legacyAPISupported)
94 { 117 {
118 // Another update can be requested while one is still in progress (e.g. the
119 // user adds filter lists in quick succession). When this happens, save the
120 // request and execute it later.
121 if (afterContentBlockingFinished)
122 {
123 pendingContentBlockerUpdate = {
124 params: Array.from(arguments),
125 // Save the current dirty state so we can set it later before calling
126 // this function again.
127 setDirty: contentBlockListDirty
128 };
129 return;
130 }
131
95 afterContentBlockingFinished = new Promise(resolve => 132 afterContentBlockingFinished = new Promise(resolve =>
96 { 133 {
97 setContentBlocker(error => 134 setContentBlocker().catch(error =>
98 { 135 {
99 if (error instanceof Error) 136 if (error instanceof Error)
Manish Jethani 2017/08/16 10:23:02 The code was ignoring errors that weren't instance
Sebastian Noack 2017/08/16 10:30:34 It seems, it would be simpler, if you move that lo
Manish Jethani 2017/08/16 11:13:38 Done.
100 { 137 throw error;
101 let suppressErrorMessage = false; 138 })
102 139 // There's no need for a catch handler at the end because the handler is
103 // If the content blocking API fails the first time it's used the 140 // not expected to throw any errors. If this changes, however, then the
104 // legacy blocking API (if available) won't have been disabled. 141 // error must be handled appropriately.
105 if (!contentBlockingActive && legacyAPISupported) 142 .then(() =>
106 { 143 {
107 Prefs.safariContentBlocker = false; 144 if (!contentBlockingActive)
108 // If content blocking failed on startup and we're switching back to
109 // the legacy API anyway we don't need to show an error message.
110 if (isStartup)
111 suppressErrorMessage = true;
112 }
113
114 if (!suppressErrorMessage)
115 alert(error.message);
116 }
117 else if (!contentBlockingActive)
118 { 145 {
119 contentBlockingActive = true; 146 contentBlockingActive = true;
120 clearBlockCounters(); 147 clearBlockCounters();
121 } 148 }
149 },
150 error =>
151 {
152 let suppressErrorMessage = false;
122 153
154 // If the content blocking API fails the first time it's used the
155 // legacy blocking API (if available) won't have been disabled.
156 if (!contentBlockingActive && legacyAPISupported)
157 {
158 Prefs.safariContentBlocker = false;
159 // If content blocking failed on startup and we're switching back to
160 // the legacy API anyway we don't need to show an error message.
161 if (isStartup)
162 suppressErrorMessage = true;
163 }
164
165 if (!suppressErrorMessage)
166 alert(error.message);
167 })
168 .then(() =>
169 {
170 afterContentBlockingFinished = null;
Manish Jethani 2017/08/16 10:23:02 Code that should run regardless of success/failure
123 resolve(contentBlockingActive); 171 resolve(contentBlockingActive);
124 afterContentBlockingFinished = null;
125 }); 172 });
173 })
174 .then(active =>
Sebastian Noack 2017/08/16 10:30:34 I wonder if this logic, couldn't just go into the
Manish Jethani 2017/08/16 11:13:38 You're right, done. Note that we have to add a ca
175 {
176 // If there's another update pending, execute it now.
177 if (pendingContentBlockerUpdate)
178 {
179 let {params, setDirty} = pendingContentBlockerUpdate;
180 pendingContentBlockerUpdate = null;
181
182 if (setDirty)
183 contentBlockListDirty = true;
184
185 updateContentBlocker.apply(null, params);
186 return afterContentBlockingFinished;
187 }
188
189 return active;
126 }); 190 });
127 } 191 }
128 192
129 if (contentBlockingSupported) 193 if (contentBlockingSupported)
130 { 194 {
131 Promise.all([Prefs.untilLoaded, 195 Promise.all([Prefs.untilLoaded,
132 FilterNotifier.once("load"), 196 FilterNotifier.once("load"),
133 legacyAPISupported]).then(resolvedValues => 197 legacyAPISupported]).then(resolvedValues =>
134 { 198 {
135 let legacyAPISupported = resolvedValues[2]; 199 let legacyAPISupported = resolvedValues[2];
(...skipping 17 matching lines...) Expand all
153 }); 217 });
154 }); 218 });
155 } 219 }
156 220
157 port.on("safari.contentBlockingActive", (msg, sender) => 221 port.on("safari.contentBlockingActive", (msg, sender) =>
158 { 222 {
159 if (!contentBlockingActive && afterContentBlockingFinished) 223 if (!contentBlockingActive && afterContentBlockingFinished)
160 return afterContentBlockingFinished; 224 return afterContentBlockingFinished;
161 return contentBlockingActive; 225 return contentBlockingActive;
162 }); 226 });
OLDNEW
« no previous file with comments | « lib/requestBlocker.js ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld