Left: | ||
Right: |
OLD | NEW |
---|---|
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-2015 Eyeo GmbH | 3 * Copyright (C) 2006-2015 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 package org.adblockplus.browser; | 18 package org.adblockplus.browser; |
19 | 19 |
20 import java.util.ArrayList; | |
20 import java.util.HashMap; | 21 import java.util.HashMap; |
22 import java.util.List; | |
21 import java.util.Map; | 23 import java.util.Map; |
22 | 24 |
23 import android.annotation.SuppressLint; | 25 import android.annotation.SuppressLint; |
26 import android.content.Context; | |
27 import android.content.SharedPreferences; | |
24 import android.os.Handler; | 28 import android.os.Handler; |
25 import android.os.HandlerThread; | 29 import android.os.HandlerThread; |
26 import android.util.Log; | 30 import android.util.Log; |
27 | 31 |
32 import org.json.JSONArray; | |
28 import org.json.JSONException; | 33 import org.json.JSONException; |
29 import org.json.JSONObject; | 34 import org.json.JSONObject; |
35 import org.mozilla.gecko.EventDispatcher; | |
30 import org.mozilla.gecko.GeckoAppShell; | 36 import org.mozilla.gecko.GeckoAppShell; |
37 import org.mozilla.gecko.util.GeckoEventListener; | |
31 import org.mozilla.gecko.util.GeckoRequest; | 38 import org.mozilla.gecko.util.GeckoRequest; |
32 import org.mozilla.gecko.util.NativeJSObject; | 39 import org.mozilla.gecko.util.NativeJSObject; |
33 | 40 |
34 @SuppressLint("DefaultLocale") | 41 @SuppressLint("DefaultLocale") |
35 public class AddOnBridge | 42 public class AddOnBridge |
36 { | 43 { |
37 private static final String TAG = "AdblockBrowser.AddOnBridge"; | 44 private static final String TAG = "AdblockBrowser.AddOnBridge"; |
38 private static final String REQUEST_NAME = "AdblockPlus:Api"; | 45 private static final String REQUEST_NAME = "AdblockPlus:Api"; |
39 // Timeout for checking filter loading (in seconds) | 46 // Timeout for checking filter loading (in seconds) |
40 private static final int QUERY_GET_FILTERS_LOADED_TIMEOUT = 30; | 47 private static final int QUERY_GET_FILTERS_LOADED_TIMEOUT = 30; |
41 // How long to wait between retries (in milliseconds) | 48 // How long to wait between retries (in milliseconds) |
42 private static final int QUERY_GET_FILTERS_LOADED_DELAY = 500; | 49 private static final int QUERY_GET_FILTERS_LOADED_DELAY = 500; |
43 // Handler+HandlerThread for posting delayed re-tries without interfering with | 50 // Handler+HandlerThread for posting delayed re-tries without interfering with |
44 // other threads (e.g. the UI or Gecko thread) | 51 // other threads (e.g. the UI or Gecko thread) |
45 private static final HandlerThread PRIVATE_HANDLER_THREAD; | 52 private static final HandlerThread PRIVATE_HANDLER_THREAD; |
46 private static final Handler PRIVATE_HANDLER; | 53 private static final Handler PRIVATE_HANDLER; |
47 // Global handler, for e.g. UI tasks | 54 // Global handler, for e.g. UI tasks |
48 private static final HandlerThread GLOBAL_HANDLER_THREAD; | 55 private static final HandlerThread GLOBAL_HANDLER_THREAD; |
49 private static final Handler GLOBAL_HANDLER; | 56 private static final Handler GLOBAL_HANDLER; |
57 // Sometimes, the app is killed before the extension is able to save all chang es regarding | |
58 // AddOnBridge requests. Given that, we need to store the uncompleted requests on SharedPrefs, | |
59 // so we can resend them to the extension once the app restarts | |
60 // See https://issues.adblockplus.org/ticket/2853 | |
61 private static final String ON_SAVE_EVENT = "Abb:OnSave"; | |
62 private static final List<ChainedRequest> UNCOMPLETED_REQUESTS = new ArrayList <>(); | |
63 private static final String UNCOMPLETED_REQUESTS_PREFS_KEY = "UNCOMPLETED_REQU ESTS_PREFS_KEY"; | |
64 | |
65 private static SharedPreferences sharedPrefs; | |
50 | 66 |
51 static | 67 static |
52 { | 68 { |
53 PRIVATE_HANDLER_THREAD = new HandlerThread("abp-private-handler"); | 69 PRIVATE_HANDLER_THREAD = new HandlerThread("abp-private-handler"); |
54 PRIVATE_HANDLER_THREAD.setDaemon(true); | 70 PRIVATE_HANDLER_THREAD.setDaemon(true); |
55 PRIVATE_HANDLER_THREAD.start(); | 71 PRIVATE_HANDLER_THREAD.start(); |
56 PRIVATE_HANDLER = new Handler(PRIVATE_HANDLER_THREAD.getLooper()); | 72 PRIVATE_HANDLER = new Handler(PRIVATE_HANDLER_THREAD.getLooper()); |
57 | 73 |
58 GLOBAL_HANDLER_THREAD = new HandlerThread("abp-global-handler"); | 74 GLOBAL_HANDLER_THREAD = new HandlerThread("abp-global-handler"); |
59 GLOBAL_HANDLER_THREAD.setDaemon(true); | 75 GLOBAL_HANDLER_THREAD.setDaemon(true); |
60 GLOBAL_HANDLER_THREAD.start(); | 76 GLOBAL_HANDLER_THREAD.start(); |
61 GLOBAL_HANDLER = new Handler(GLOBAL_HANDLER_THREAD.getLooper()); | 77 GLOBAL_HANDLER = new Handler(GLOBAL_HANDLER_THREAD.getLooper()); |
62 } | 78 } |
63 | 79 |
80 public static void init(Context context) | |
81 { | |
82 sharedPrefs = context.getSharedPreferences(AddOnBridge.class.getName(), Cont ext.MODE_PRIVATE); | |
83 EventDispatcher.getInstance().registerGeckoThreadListener(new AddOnEventList ener(), ON_SAVE_EVENT); | |
84 loadAndResendUncompletedRequests(); | |
85 } | |
86 | |
64 public static void postToHandler(Runnable runnable) | 87 public static void postToHandler(Runnable runnable) |
65 { | 88 { |
66 GLOBAL_HANDLER.post(runnable); | 89 GLOBAL_HANDLER.post(runnable); |
67 } | 90 } |
68 | 91 |
69 public static void postToHandlerDelayed(Runnable runnable, long delayMillis) | 92 public static void postToHandlerDelayed(Runnable runnable, long delayMillis) |
70 { | 93 { |
71 GLOBAL_HANDLER.postDelayed(runnable, delayMillis); | 94 GLOBAL_HANDLER.postDelayed(runnable, delayMillis); |
72 } | 95 } |
73 | 96 |
97 private static void loadAndResendUncompletedRequests() | |
98 { | |
99 postToHandler(new Runnable() | |
100 { | |
101 @Override | |
102 public void run() | |
103 { | |
104 final String jsonString = sharedPrefs.getString(UNCOMPLETED_REQUESTS_PRE FS_KEY, null); | |
105 UNCOMPLETED_REQUESTS.clear(); | |
106 UNCOMPLETED_REQUESTS.addAll(jsonStringToRequestList(jsonString)); | |
107 for (final ChainedRequest request : UNCOMPLETED_REQUESTS) | |
108 { | |
109 GeckoAppShell.sendRequestToGecko(request); | |
110 } | |
111 } | |
112 }); | |
113 } | |
114 | |
115 private static void clearUncompletedRequests() | |
116 { | |
117 postToHandler(new Runnable() | |
118 { | |
119 @Override | |
120 public void run() | |
121 { | |
122 if (!UNCOMPLETED_REQUESTS.isEmpty()) | |
123 { | |
124 UNCOMPLETED_REQUESTS.clear(); | |
125 storeStringPref(UNCOMPLETED_REQUESTS_PREFS_KEY, requestListToJsonStrin g(UNCOMPLETED_REQUESTS)); | |
126 } | |
127 } | |
128 }); | |
129 } | |
130 | |
131 private static void addUncompletedRequest(final ChainedRequest chainedRequest) | |
132 { | |
133 postToHandler(new Runnable() | |
134 { | |
135 @Override | |
136 public void run() | |
137 { | |
138 UNCOMPLETED_REQUESTS.add(chainedRequest); | |
139 storeStringPref(UNCOMPLETED_REQUESTS_PREFS_KEY, requestListToJsonString( UNCOMPLETED_REQUESTS)); | |
140 } | |
141 }); | |
142 } | |
143 | |
144 private static String requestListToJsonString(final List<ChainedRequest> reque stList) | |
145 { | |
146 if (requestList == null) | |
147 { | |
148 return null; | |
149 } | |
150 final JSONArray jsonArray = new JSONArray(); | |
151 for (final ChainedRequest request : requestList) | |
152 { | |
153 jsonArray.put(request.value); | |
154 } | |
155 return jsonArray.toString(); | |
156 } | |
157 | |
158 private static List<ChainedRequest> jsonStringToRequestList(final String jsonS tring) | |
159 { | |
160 final List<ChainedRequest> requestList = new ArrayList<>(); | |
161 if (jsonString == null) | |
162 { | |
163 return requestList; | |
164 } | |
165 try | |
166 { | |
167 final JSONArray jsonArray = new JSONArray(jsonString); | |
168 for (int i = 0; i < jsonArray.length(); i++) | |
169 { | |
170 final ChainedRequest request = new ChainedRequest(jsonArray.getJSONObjec t(i), null); | |
171 requestList.add(request); | |
172 } | |
173 } | |
174 catch (JSONException e) | |
175 { | |
176 } | |
177 return requestList; | |
178 } | |
179 | |
180 private static void storeStringPref(String key, String value) | |
181 { | |
182 final SharedPreferences.Editor editor = sharedPrefs.edit(); | |
183 editor.putString(key, value); | |
184 editor.commit(); | |
185 } | |
186 | |
74 public static boolean getBooleanFromJsObject(final NativeJSObject obj, final S tring name, | 187 public static boolean getBooleanFromJsObject(final NativeJSObject obj, final S tring name, |
75 final boolean defaultValue) | 188 final boolean defaultValue) |
76 { | 189 { |
77 try | 190 try |
78 { | 191 { |
79 return obj.getBoolean(name); | 192 return obj.getBoolean(name); |
80 } | 193 } |
81 catch (final Exception e) | 194 catch (final Exception e) |
82 { | 195 { |
83 return defaultValue; | 196 return defaultValue; |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
142 GeckoAppShell.sendRequestToGecko( | 255 GeckoAppShell.sendRequestToGecko( |
143 new ChainedRequest( | 256 new ChainedRequest( |
144 createRequestData("get" + makeFirstCharacterUppercase(name)), | 257 createRequestData("get" + makeFirstCharacterUppercase(name)), |
145 callback)); | 258 callback)); |
146 } | 259 } |
147 | 260 |
148 public static void setBoolean(final AdblockPlusApiCallback callback, final Str ing name, | 261 public static void setBoolean(final AdblockPlusApiCallback callback, final Str ing name, |
149 final boolean enable) | 262 final boolean enable) |
150 { | 263 { |
151 Log.d(TAG, "setBoolean " + enable + " for " + name); | 264 Log.d(TAG, "setBoolean " + enable + " for " + name); |
152 GeckoAppShell.sendRequestToGecko( | 265 final ChainedRequest request = |
153 new ChainedRequest( | 266 new ChainedRequest(createRequestData("set" + makeFirstCharacterUppercase (name), enable), callback); |
154 createRequestData("set" + makeFirstCharacterUppercase(name), enable) , | 267 GeckoAppShell.sendRequestToGecko(request); |
155 callback)); | 268 addUncompletedRequest(request); |
156 } | 269 } |
157 | 270 |
158 private static void callFunction(final AdblockPlusApiCallback callback, final String name, | 271 private static void callFunction(final AdblockPlusApiCallback callback, final String name, |
159 final Map<String, Object> parameters) | 272 final Map<String, Object> parameters) |
160 { | 273 { |
274 // By default, requests are not added to the uncompleted request list. This should apply for | |
275 // requests that doesn't result in save operations performed by the extensio n | |
276 callFunction(callback, name, parameters, false); | |
277 } | |
278 | |
279 private static void callFunction(final AdblockPlusApiCallback callback, final String name, | |
280 final Map<String, Object> parameters, boolean shouldAddUncompletedRequest) | |
Felix Dahlke
2016/12/21 20:23:25
Nit: `shouldAddUncompletedRequest` sounds a bit li
diegocarloslima
2016/12/21 20:29:31
Acknowledged.
| |
281 { | |
161 final JSONObject requestData = createRequestData(name); | 282 final JSONObject requestData = createRequestData(name); |
162 try | 283 try |
163 { | 284 { |
164 for (Map.Entry<String, Object> entry : parameters.entrySet()) | 285 for (Map.Entry<String, Object> entry : parameters.entrySet()) |
165 requestData.put(entry.getKey(), entry.getValue()); | 286 requestData.put(entry.getKey(), entry.getValue()); |
166 } | 287 } |
167 catch (JSONException e) | 288 catch (JSONException e) |
168 { | 289 { |
169 // we're only adding sane objects | 290 // we're only adding sane objects |
170 Log.e(TAG, "Creating request data failed with: " + e.getMessage(), e); | 291 Log.e(TAG, "Creating request data failed with: " + e.getMessage(), e); |
171 } | 292 } |
172 GeckoAppShell.sendRequestToGecko(new ChainedRequest(requestData, callback)); | 293 final ChainedRequest request = new ChainedRequest(requestData, callback); |
294 GeckoAppShell.sendRequestToGecko(request); | |
295 if (shouldAddUncompletedRequest) | |
296 { | |
297 addUncompletedRequest(request); | |
298 } | |
173 } | 299 } |
174 | 300 |
175 public static void querySubscriptionListStatus(final AdblockPlusApiCallback ca llback, | 301 public static void querySubscriptionListStatus(final AdblockPlusApiCallback ca llback, |
176 final String url) | 302 final String url) |
177 { | 303 { |
178 Log.d(TAG, "querySubscriptionListStatus for " + url); | 304 Log.d(TAG, "querySubscriptionListStatus for " + url); |
179 final Map<String, Object> parameters = new HashMap<String, Object>(); | 305 final Map<String, Object> parameters = new HashMap<String, Object>(); |
180 parameters.put("url", url); | 306 parameters.put("url", url); |
181 callFunction(callback, "isSubscriptionListed", parameters); | 307 callFunction(callback, "isSubscriptionListed", parameters); |
182 } | 308 } |
183 | 309 |
184 public static void addSubscription(final AdblockPlusApiCallback callback, | 310 public static void addSubscription(final AdblockPlusApiCallback callback, |
185 final String url, final String title) | 311 final String url, final String title) |
186 { | 312 { |
187 Log.d(TAG, "addSubscription for " + url + " (" + title + ")"); | 313 Log.d(TAG, "addSubscription for " + url + " (" + title + ")"); |
188 final Map<String, Object> parameters = new HashMap<String, Object>(); | 314 final Map<String, Object> parameters = new HashMap<String, Object>(); |
189 parameters.put("url", url); | 315 parameters.put("url", url); |
190 if (title != null) | 316 if (title != null) |
191 { | 317 { |
192 parameters.put("title", title); | 318 parameters.put("title", title); |
193 } | 319 } |
194 callFunction(callback, "addSubscription", parameters); | 320 callFunction(callback, "addSubscription", parameters, true); |
195 } | 321 } |
196 | 322 |
197 public static void queryActiveSubscriptions(final AdblockPlusApiCallback callb ack) | 323 public static void queryActiveSubscriptions(final AdblockPlusApiCallback callb ack) |
198 { | 324 { |
199 Log.d(TAG, "queryActiveSubscriptions"); | 325 Log.d(TAG, "queryActiveSubscriptions"); |
200 final Map<String, Object> parameters = new HashMap<String, Object>(); | 326 final Map<String, Object> parameters = new HashMap<String, Object>(); |
201 callFunction(callback, "getActiveSubscriptions", parameters); | 327 callFunction(callback, "getActiveSubscriptions", parameters); |
202 } | 328 } |
203 | 329 |
204 public static void removeSubscription(final AdblockPlusApiCallback callback, | 330 public static void removeSubscription(final AdblockPlusApiCallback callback, |
205 final String url) | 331 final String url) |
206 { | 332 { |
207 Log.d(TAG, "removeSubscription for " + url); | 333 Log.d(TAG, "removeSubscription for " + url); |
208 final Map<String, Object> parameters = new HashMap<String, Object>(); | 334 final Map<String, Object> parameters = new HashMap<String, Object>(); |
209 parameters.put("url", url); | 335 parameters.put("url", url); |
210 callFunction(callback, "removeSubscription", parameters); | 336 callFunction(callback, "removeSubscription", parameters, true); |
211 } | 337 } |
212 | 338 |
213 public static void queryIsLocal(final AdblockPlusApiCallback callback, | 339 public static void queryIsLocal(final AdblockPlusApiCallback callback, |
214 final String url) | 340 final String url) |
215 { | 341 { |
216 Log.d(TAG, "queryIsLocal for " + url); | 342 Log.d(TAG, "queryIsLocal for " + url); |
217 final Map<String, Object> parameters = new HashMap<String, Object>(); | 343 final Map<String, Object> parameters = new HashMap<String, Object>(); |
218 parameters.put("url", url); | 344 parameters.put("url", url); |
219 callFunction(callback, "isLocal", parameters); | 345 callFunction(callback, "isLocal", parameters); |
220 } | 346 } |
221 | 347 |
222 public static void queryIsPageWhitelisted(final AdblockPlusApiCallback callbac k, | 348 public static void queryIsPageWhitelisted(final AdblockPlusApiCallback callbac k, |
223 final String url) | 349 final String url) |
224 { | 350 { |
225 Log.d(TAG, "queryIsPageWhitelisted for " + url); | 351 Log.d(TAG, "queryIsPageWhitelisted for " + url); |
226 final Map<String, Object> parameters = new HashMap<String, Object>(); | 352 final Map<String, Object> parameters = new HashMap<String, Object>(); |
227 parameters.put("url", url); | 353 parameters.put("url", url); |
228 callFunction(callback, "isPageWhitelisted", parameters); | 354 callFunction(callback, "isPageWhitelisted", parameters); |
229 } | 355 } |
230 | 356 |
231 public static void whitelistSite(final AdblockPlusApiCallback callback, final String url, | 357 public static void whitelistSite(final AdblockPlusApiCallback callback, final String url, |
232 final boolean whitelisted) | 358 final boolean whitelisted) |
233 { | 359 { |
234 Log.d(TAG, "whitelistSite for " + url); | 360 Log.d(TAG, "whitelistSite for " + url); |
235 final Map<String, Object> parameters = new HashMap<String, Object>(); | 361 final Map<String, Object> parameters = new HashMap<String, Object>(); |
236 parameters.put("url", url); | 362 parameters.put("url", url); |
237 parameters.put("whitelisted", whitelisted); | 363 parameters.put("whitelisted", whitelisted); |
238 callFunction(callback, "whitelistSite", parameters); | 364 callFunction(callback, "whitelistSite", parameters, true); |
239 } | 365 } |
240 | 366 |
241 private static class ChainedRequest extends GeckoRequest | 367 private static class ChainedRequest extends GeckoRequest |
242 { | 368 { |
243 private final JSONObject value; | 369 private final JSONObject value; |
244 private final AdblockPlusApiCallback apiCallback; | 370 private final AdblockPlusApiCallback apiCallback; |
245 private final boolean checkForFiltersLoaded; | 371 private final boolean checkForFiltersLoaded; |
246 private final long creationTime; | 372 private final long creationTime; |
247 | 373 |
248 public ChainedRequest(final JSONObject value, final AdblockPlusApiCallback c allback, | 374 public ChainedRequest(final JSONObject value, final AdblockPlusApiCallback c allback, |
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
356 { | 482 { |
357 this.invokeSuccessCallback(jsObject); | 483 this.invokeSuccessCallback(jsObject); |
358 } | 484 } |
359 else | 485 else |
360 { | 486 { |
361 this.invokeFailureCallback(jsObject); | 487 this.invokeFailureCallback(jsObject); |
362 } | 488 } |
363 } | 489 } |
364 } | 490 } |
365 } | 491 } |
492 | |
493 private static class AddOnEventListener implements GeckoEventListener | |
494 { | |
495 @Override | |
496 public void handleMessage(String event, JSONObject message) | |
497 { | |
498 if (ON_SAVE_EVENT.equals(event)) | |
499 { | |
500 // This indicates that all changes have been saved by the extension. Tha t way, we can clear | |
501 // our list of uncompleted requests | |
502 // See https://issues.adblockplus.org/ticket/2853 | |
503 clearUncompletedRequests(); | |
504 } | |
505 } | |
506 } | |
366 } | 507 } |
OLD | NEW |