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-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 Loading... |
30 { | 30 { |
31 class FilterEngine; | 31 class FilterEngine; |
32 typedef std::shared_ptr<FilterEngine> FilterEnginePtr; | 32 typedef std::shared_ptr<FilterEngine> FilterEnginePtr; |
33 | 33 |
34 /** | 34 /** |
35 * Wrapper for an Adblock Plus filter object. | 35 * Wrapper for an Adblock Plus filter object. |
36 * There are no accessors for most | 36 * There are no accessors for most |
37 * [filter properties](https://adblockplus.org/jsdoc/adblockpluscore/Filter.ht
ml), | 37 * [filter properties](https://adblockplus.org/jsdoc/adblockpluscore/Filter.ht
ml), |
38 * use `GetProperty()` to retrieve them by name. | 38 * use `GetProperty()` to retrieve them by name. |
39 */ | 39 */ |
40 class Filter : public JsValue, | 40 class Filter : public JsValue |
41 public std::enable_shared_from_this<Filter> | |
42 { | 41 { |
| 42 friend class FilterEngine; |
43 public: | 43 public: |
| 44 Filter(const Filter& src); |
| 45 Filter(Filter&& src); |
| 46 Filter& operator=(const Filter& src); |
| 47 Filter& operator=(Filter&& src); |
| 48 |
44 /** | 49 /** |
45 * Filter types, see https://adblockplus.org/en/filters. | 50 * Filter types, see https://adblockplus.org/en/filters. |
46 */ | 51 */ |
47 enum Type {TYPE_BLOCKING, TYPE_EXCEPTION, | 52 enum Type {TYPE_BLOCKING, TYPE_EXCEPTION, |
48 TYPE_ELEMHIDE, TYPE_ELEMHIDE_EXCEPTION, | 53 TYPE_ELEMHIDE, TYPE_ELEMHIDE_EXCEPTION, |
49 TYPE_COMMENT, TYPE_INVALID}; | 54 TYPE_COMMENT, TYPE_INVALID}; |
50 | 55 |
51 /** | 56 /** |
52 * Retrieves the type of this filter. | 57 * Retrieves the type of this filter. |
53 * @return Type of this filter. | 58 * @return Type of this filter. |
(...skipping 11 matching lines...) Expand all Loading... |
65 */ | 70 */ |
66 void AddToList(); | 71 void AddToList(); |
67 | 72 |
68 /** | 73 /** |
69 * Removes this filter from the list of custom filters. | 74 * Removes this filter from the list of custom filters. |
70 */ | 75 */ |
71 void RemoveFromList(); | 76 void RemoveFromList(); |
72 | 77 |
73 bool operator==(const Filter& filter) const; | 78 bool operator==(const Filter& filter) const; |
74 | 79 |
| 80 protected: |
75 /** | 81 /** |
76 * Creates a wrapper for an existing JavaScript filter object. | 82 * Creates a wrapper for an existing JavaScript filter object. |
77 * Normally you shouldn't call this directly, but use | 83 * Normally you shouldn't call this directly, but use |
78 * FilterEngine::GetFilter() instead. | 84 * FilterEngine::GetFilter() instead. |
79 * @param value JavaScript filter object. | 85 * @param value JavaScript filter object. |
80 */ | 86 */ |
81 Filter(JsValue&& value); | 87 Filter(JsValue&& value); |
82 }; | 88 }; |
83 | 89 |
84 /** | 90 /** |
85 * Wrapper for a subscription object. | 91 * Wrapper for a subscription object. |
86 * There are no accessors for most | 92 * There are no accessors for most |
87 * [subscription properties](https://adblockplus.org/jsdoc/adblockpluscore/Sub
scription.html), | 93 * [subscription properties](https://adblockplus.org/jsdoc/adblockpluscore/Sub
scription.html), |
88 * use `GetProperty()` to retrieve them by name. | 94 * use `GetProperty()` to retrieve them by name. |
89 */ | 95 */ |
90 class Subscription : public JsValue, | 96 class Subscription : public JsValue |
91 public std::enable_shared_from_this<Subscription> | |
92 { | 97 { |
| 98 friend class FilterEngine; |
93 public: | 99 public: |
94 /** | 100 /** |
| 101 * Copy constructor |
| 102 */ |
| 103 Subscription(const Subscription& src); |
| 104 |
| 105 /** |
| 106 * Move constructor |
| 107 */ |
| 108 Subscription(Subscription&& src); |
| 109 |
| 110 /** |
| 111 * Assignment operator |
| 112 */ |
| 113 Subscription& operator=(const Subscription& src); |
| 114 |
| 115 /** |
| 116 * Move assignment operator |
| 117 */ |
| 118 Subscription& operator=(Subscription&& src); |
| 119 |
| 120 /** |
95 * Checks if this subscription has been added to the list of subscriptions. | 121 * Checks if this subscription has been added to the list of subscriptions. |
96 * @return `true` if this subscription has been added. | 122 * @return `true` if this subscription has been added. |
97 */ | 123 */ |
98 bool IsListed() const; | 124 bool IsListed() const; |
99 | 125 |
100 /** | 126 /** |
101 * Adds this subscription to the list of subscriptions. | 127 * Adds this subscription to the list of subscriptions. |
102 */ | 128 */ |
103 void AddToList(); | 129 void AddToList(); |
104 | 130 |
105 /** | 131 /** |
106 * Removes this subscription from the list of subscriptions. | 132 * Removes this subscription from the list of subscriptions. |
107 */ | 133 */ |
108 void RemoveFromList(); | 134 void RemoveFromList(); |
109 | 135 |
110 /** | 136 /** |
111 * Updates this subscription, i.e.\ retrieves the current filters from the | 137 * Updates this subscription, i.e.\ retrieves the current filters from the |
112 * subscription URL. | 138 * subscription URL. |
113 */ | 139 */ |
114 void UpdateFilters(); | 140 void UpdateFilters(); |
115 | 141 |
116 /** | 142 /** |
117 * Checks if the subscription is currently being updated. | 143 * Checks if the subscription is currently being updated. |
118 * @return `true` if the subscription is currently being updated. | 144 * @return `true` if the subscription is currently being updated. |
119 */ | 145 */ |
120 bool IsUpdating() const; | 146 bool IsUpdating() const; |
121 | 147 |
122 /** | 148 /** |
123 * Indicates whether the subscription is acceptable ads subscription. | 149 * Indicates whether the subscription is the Acceptable Ads subscription. |
124 * @return `true` if this subscription is acceptable ads subscription. | 150 * @return `true` if this subscription is the Acceptable Ads subscription. |
125 */ | 151 */ |
126 bool IsAA() const; | 152 bool IsAA() const; |
127 | 153 |
128 bool operator==(const Subscription& subscription) const; | 154 bool operator==(const Subscription& subscription) const; |
129 | 155 |
| 156 protected: |
130 /** | 157 /** |
131 * Creates a wrapper for an existing JavaScript subscription object. | 158 * Creates a wrapper for an existing JavaScript subscription object. |
132 * Normally you shouldn't call this directly, but use | 159 * Normally you shouldn't call this directly, but use |
133 * FilterEngine::GetSubscription() instead. | 160 * FilterEngine::GetSubscription() instead. |
134 * @param value JavaScript subscription object. | 161 * @param value JavaScript subscription object. |
135 */ | 162 */ |
136 Subscription(JsValue&& value); | 163 Subscription(JsValue&& value); |
137 }; | 164 }; |
138 | 165 |
139 /** | 166 /** |
140 * Shared smart pointer to a `Filter` instance. | 167 * A smart pointer to a `Filter` instance. |
141 */ | 168 */ |
142 typedef std::shared_ptr<Filter> FilterPtr; | 169 typedef std::unique_ptr<Filter> FilterPtr; |
143 | |
144 /** | |
145 * Shared smart pointer to a `Subscription` instance. | |
146 */ | |
147 typedef std::shared_ptr<Subscription> SubscriptionPtr; | |
148 | 170 |
149 /** | 171 /** |
150 * Main component of libadblockplus. | 172 * Main component of libadblockplus. |
151 * It handles: | 173 * It handles: |
152 * - Filter management and matching. | 174 * - Filter management and matching. |
153 * - Subscription management and synchronization. | 175 * - Subscription management and synchronization. |
154 * - Update checks for the application. | 176 * - Update checks for the application. |
155 */ | 177 */ |
156 class FilterEngine | 178 class FilterEngine |
157 { | 179 { |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
199 */ | 221 */ |
200 typedef std::function<void(const std::string&)> UpdateCheckDoneCallback; | 222 typedef std::function<void(const std::string&)> UpdateCheckDoneCallback; |
201 | 223 |
202 /** | 224 /** |
203 * Callback type invoked when the filters change. | 225 * Callback type invoked when the filters change. |
204 * The first parameter is the action event code (see | 226 * The first parameter is the action event code (see |
205 * [FilterNotifier.triggerListeners](https://adblockplus.org/jsdoc/adblockpl
uscore/FilterNotifier.html#.triggerListeners) | 227 * [FilterNotifier.triggerListeners](https://adblockplus.org/jsdoc/adblockpl
uscore/FilterNotifier.html#.triggerListeners) |
206 * for the full list). | 228 * for the full list). |
207 * The second parameter is the filter/subscription object affected, if any. | 229 * The second parameter is the filter/subscription object affected, if any. |
208 */ | 230 */ |
209 typedef std::function<void(const std::string&, const JsValuePtr)> FilterChan
geCallback; | 231 typedef std::function<void(const std::string&, const JsValue&)> FilterChange
Callback; |
210 | 232 |
211 /** | 233 /** |
212 * Container of name-value pairs representing a set of preferences. | 234 * Container of name-value pairs representing a set of preferences. |
213 */ | 235 */ |
214 typedef std::map<std::string, AdblockPlus::JsValuePtr> Prefs; | 236 typedef std::map<std::string, AdblockPlus::JsValue> Prefs; |
215 | 237 |
216 /** | 238 /** |
217 * Callback type invoked when a new notification should be shown. | 239 * Callback type invoked when a new notification should be shown. |
218 * The parameter is the Notification object to be shown. | 240 * The parameter is the Notification object to be shown. |
219 */ | 241 */ |
220 typedef std::function<void(const NotificationPtr&)> ShowNotificationCallback
; | 242 typedef std::function<void(Notification&)> ShowNotificationCallback; |
221 | 243 |
222 /** | 244 /** |
223 * Callback function returning false when current connection is not | 245 * Callback function returning false when current connection is not |
224 * allowedConnectionType, e.g. because it is a metered connection. | 246 * allowedConnectionType, e.g. because it is a metered connection. |
225 */ | 247 */ |
226 typedef std::function<bool(const std::string* allowedConnectionType)> IsConn
ectionAllowedCallback; | 248 typedef std::function<bool(const std::string* allowedConnectionType)> IsConn
ectionAllowedCallback; |
227 | 249 |
228 /** | 250 /** |
229 * FilterEngine creation parameters. | 251 * FilterEngine creation parameters. |
230 */ | 252 */ |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
278 * @return `true` if the application is running for the first time. | 300 * @return `true` if the application is running for the first time. |
279 */ | 301 */ |
280 bool IsFirstRun() const; | 302 bool IsFirstRun() const; |
281 | 303 |
282 /** | 304 /** |
283 * Retrieves a filter object from its text representation. | 305 * Retrieves a filter object from its text representation. |
284 * @param text Text representation of the filter, | 306 * @param text Text representation of the filter, |
285 * see https://adblockplus.org/en/filters. | 307 * see https://adblockplus.org/en/filters. |
286 * @return New `Filter` instance. | 308 * @return New `Filter` instance. |
287 */ | 309 */ |
288 FilterPtr GetFilter(const std::string& text) const; | 310 Filter GetFilter(const std::string& text) const; |
289 | 311 |
290 /** | 312 /** |
291 * Retrieves a subscription object for the supplied URL. | 313 * Retrieves a subscription object for the supplied URL. |
292 * @param url Subscription URL. | 314 * @param url Subscription URL. |
293 * @return New `Subscription` instance. | 315 * @return New `Subscription` instance. |
294 */ | 316 */ |
295 SubscriptionPtr GetSubscription(const std::string& url) const; | 317 Subscription GetSubscription(const std::string& url) const; |
296 | 318 |
297 /** | 319 /** |
298 * Retrieves the list of custom filters. | 320 * Retrieves the list of custom filters. |
299 * @return List of custom filters. | 321 * @return List of custom filters. |
300 */ | 322 */ |
301 std::vector<FilterPtr> GetListedFilters() const; | 323 std::vector<Filter> GetListedFilters() const; |
302 | 324 |
303 /** | 325 /** |
304 * Retrieves all subscriptions. | 326 * Retrieves all subscriptions. |
305 * @return List of subscriptions. | 327 * @return List of subscriptions. |
306 */ | 328 */ |
307 std::vector<SubscriptionPtr> GetListedSubscriptions() const; | 329 std::vector<Subscription> GetListedSubscriptions() const; |
308 | 330 |
309 /** | 331 /** |
310 * Retrieves all recommended subscriptions. | 332 * Retrieves all recommended subscriptions. |
311 * @return List of recommended subscriptions. | 333 * @return List of recommended subscriptions. |
312 */ | 334 */ |
313 std::vector<SubscriptionPtr> FetchAvailableSubscriptions() const; | 335 std::vector<Subscription> FetchAvailableSubscriptions() const; |
314 | 336 |
315 /** | 337 /** |
316 * Ensures that Acceptable Ads subscription is enabled or disabled. | 338 * Ensures that the Acceptable Ads subscription is enabled or disabled. |
317 * @param enabled | 339 * @param enabled |
318 * - if the value is `true` | 340 * - if the value is `true` |
319 * - ensure that the filter set includes an enabled AA subscription, | 341 * - ensure that the filter set includes an enabled AA subscription, |
320 * adding it if needed and enabling it if disabled. | 342 * adding it if needed and enabling it if disabled. |
321 * - if the value is `false` | 343 * - if the value is `false` |
322 * - if an AA subscription is present, disable it. | 344 * - if an AA subscription is present, disable it. |
323 * - if absent, do nothing. | 345 * - if absent, do nothing. |
324 */ | 346 */ |
325 void SetAAEnabled(bool enabled); | 347 void SetAAEnabled(bool enabled); |
326 | 348 |
327 /** | 349 /** |
328 * Checks whether Acceptable Ads subscription is enabled. | 350 * Checks whether the Acceptable Ads subscription is enabled. |
329 * @return `true` if acceptable ads subscription is present and enabled. | 351 * @return `true` if the Acceptable Ads subscription is present and enabled. |
330 */ | 352 */ |
331 bool IsAAEnabled() const; | 353 bool IsAAEnabled() const; |
332 | 354 |
333 /** | 355 /** |
334 * Retrieves the URL of Acceptable Ads subscription, what makes the URL | 356 * Retrieves the URL of the Acceptable Ads subscription, what makes the URL |
335 * available even if subscription is not add yet. | 357 * available even if subscription is not added yet. |
336 * @return Returns URL of Acceptable Ads. | 358 * @return Returns URL of the Acceptable Ads. |
337 */ | 359 */ |
338 std::string GetAAUrl() const; | 360 std::string GetAAUrl() const; |
339 | 361 |
340 /** | 362 /** |
341 * Invokes the listener set via SetNotificationAvailableCallback() with the | 363 * Invokes the listener set via SetNotificationAvailableCallback() with the |
342 * next notification to be shown. | 364 * next notification to be shown. |
343 * @param url URL to match notifications to (optional). | 365 * @param url URL to match notifications to (optional). |
344 */ | 366 */ |
345 void ShowNextNotification(const std::string& url = std::string()); | 367 void ShowNextNotification(const std::string& url = std::string()) const; |
346 | 368 |
347 /** | 369 /** |
348 * Sets the callback invoked when a notification should be shown. | 370 * Sets the callback invoked when a notification should be shown. |
349 * @param callback Callback to invoke. | 371 * @param callback Callback to invoke. |
350 */ | 372 */ |
351 void SetShowNotificationCallback(const ShowNotificationCallback& value); | 373 void SetShowNotificationCallback(const ShowNotificationCallback& value); |
352 | 374 |
353 /** | 375 /** |
354 * Removes the callback invoked when a notification should be shown. | 376 * Removes the callback invoked when a notification should be shown. |
355 */ | 377 */ |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
421 * @param domain Domain to retrieve CSS selectors for. | 443 * @param domain Domain to retrieve CSS selectors for. |
422 * @return List of CSS selectors. | 444 * @return List of CSS selectors. |
423 */ | 445 */ |
424 std::vector<std::string> GetElementHidingSelectors(const std::string& domain
) const; | 446 std::vector<std::string> GetElementHidingSelectors(const std::string& domain
) const; |
425 | 447 |
426 /** | 448 /** |
427 * Retrieves a preference value. | 449 * Retrieves a preference value. |
428 * @param pref Preference name. | 450 * @param pref Preference name. |
429 * @return Preference value, or `null` if it doesn't exist. | 451 * @return Preference value, or `null` if it doesn't exist. |
430 */ | 452 */ |
431 JsValuePtr GetPref(const std::string& pref) const; | 453 JsValue GetPref(const std::string& pref) const; |
432 | 454 |
433 /** | 455 /** |
434 * Sets a preference value. | 456 * Sets a preference value. |
435 * @param pref Preference name. | 457 * @param pref Preference name. |
436 * @param value New value of the preference. | 458 * @param value New value of the preference. |
437 */ | 459 */ |
438 void SetPref(const std::string& pref, JsValuePtr value); | 460 void SetPref(const std::string& pref, const JsValue& value); |
439 | 461 |
440 /** | 462 /** |
441 * Extracts the host from a URL. | 463 * Extracts the host from a URL. |
442 * @param url URL to extract the host from. | 464 * @param url URL to extract the host from. |
443 * @return Extracted host. | 465 * @return Extracted host. |
444 */ | 466 */ |
445 std::string GetHostFromURL(const std::string& url) const; | 467 std::string GetHostFromURL(const std::string& url) const; |
446 | 468 |
447 /** | 469 /** |
448 * Sets the callback invoked when an application update becomes available. | 470 * Sets the callback invoked when an application update becomes available. |
449 * @param callback Callback to invoke. | 471 * @param callback Callback to invoke. |
450 */ | 472 */ |
451 void SetUpdateAvailableCallback(UpdateAvailableCallback callback); | 473 void SetUpdateAvailableCallback(const UpdateAvailableCallback& callback); |
452 | 474 |
453 /** | 475 /** |
454 * Removes the callback invoked when an application update becomes | 476 * Removes the callback invoked when an application update becomes |
455 * available. | 477 * available. |
456 */ | 478 */ |
457 void RemoveUpdateAvailableCallback(); | 479 void RemoveUpdateAvailableCallback(); |
458 | 480 |
459 /** | 481 /** |
460 * Forces an immediate update check. | 482 * Forces an immediate update check. |
461 * `FilterEngine` will automatically check for updates in regular intervals, | 483 * `FilterEngine` will automatically check for updates in regular intervals, |
462 * so applications should only call this when the user triggers an update | 484 * so applications should only call this when the user triggers an update |
463 * check manually. | 485 * check manually. |
464 * @param callback Optional callback to invoke when the update check is | 486 * @param callback Optional callback to invoke when the update check is |
465 * finished. The string parameter will be empty when the update check | 487 * finished. The string parameter will be empty when the update check |
466 * succeeded, or contain an error message if it failed. | 488 * succeeded, or contain an error message if it failed. |
467 * Note that the callback will be invoked whether updates are | 489 * Note that the callback will be invoked whether updates are |
468 * available or not - to react to updates being available, use | 490 * available or not - to react to updates being available, use |
469 * `FilterEngine::SetUpdateAvailableCallback()`. | 491 * `FilterEngine::SetUpdateAvailableCallback()`. |
470 */ | 492 */ |
471 void ForceUpdateCheck(const UpdateCheckDoneCallback& callback = UpdateCheckD
oneCallback()); | 493 void ForceUpdateCheck(const UpdateCheckDoneCallback& callback = UpdateCheckD
oneCallback()); |
472 | 494 |
473 /** | 495 /** |
474 * Sets the callback invoked when the filters change. | 496 * Sets the callback invoked when the filters change. |
475 * @param callback Callback to invoke. | 497 * @param callback Callback to invoke. |
476 */ | 498 */ |
477 void SetFilterChangeCallback(FilterChangeCallback callback); | 499 void SetFilterChangeCallback(const FilterChangeCallback& callback); |
478 | 500 |
479 /** | 501 /** |
480 * Removes the callback invoked when the filters change. | 502 * Removes the callback invoked when the filters change. |
481 */ | 503 */ |
482 void RemoveFilterChangeCallback(); | 504 void RemoveFilterChangeCallback(); |
483 | 505 |
484 /** | 506 /** |
485 * Stores the value indicating what connection types are allowed, it is | 507 * Stores the value indicating what connection types are allowed, it is |
486 * passed to CreateParameters::isConnectionAllowed callback. | 508 * passed to CreateParameters::isConnectionAllowed callback. |
487 * @param value Stored value. nullptr means removing of any previously | 509 * @param value Stored value. nullptr means removing of any previously |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
527 JsEnginePtr jsEngine; | 549 JsEnginePtr jsEngine; |
528 bool firstRun; | 550 bool firstRun; |
529 int updateCheckId; | 551 int updateCheckId; |
530 static const std::map<ContentType, std::string> contentTypes; | 552 static const std::map<ContentType, std::string> contentTypes; |
531 | 553 |
532 explicit FilterEngine(const JsEnginePtr& jsEngine); | 554 explicit FilterEngine(const JsEnginePtr& jsEngine); |
533 | 555 |
534 FilterPtr CheckFilterMatch(const std::string& url, | 556 FilterPtr CheckFilterMatch(const std::string& url, |
535 ContentTypeMask contentTypeMask, | 557 ContentTypeMask contentTypeMask, |
536 const std::string& documentUrl) const; | 558 const std::string& documentUrl) const; |
537 void UpdateAvailable(UpdateAvailableCallback callback, JsValueList& params); | 559 void UpdateAvailable(const UpdateAvailableCallback& callback, const JsValueL
ist& params) const; |
538 void UpdateCheckDone(const std::string& eventName, | 560 void UpdateCheckDone(const std::string& eventName, |
539 UpdateCheckDoneCallback callback, JsValueList& params); | 561 const UpdateCheckDoneCallback& callback, const JsValueL
ist& params); |
540 void FilterChanged(FilterChangeCallback callback, JsValueList& params); | 562 void FilterChanged(const FilterChangeCallback& callback, const JsValueList&
params) const; |
541 void ShowNotification(const ShowNotificationCallback& callback, | 563 void ShowNotification(const ShowNotificationCallback& callback, |
542 const JsValueList& params); | 564 const JsValueList& param) const; |
543 FilterPtr GetWhitelistingFilter(const std::string& url, | 565 FilterPtr GetWhitelistingFilter(const std::string& url, |
544 ContentTypeMask contentTypeMask, const std::string& documentUrl) const; | 566 ContentTypeMask contentTypeMask, const std::string& documentUrl) const; |
545 FilterPtr GetWhitelistingFilter(const std::string& url, | 567 FilterPtr GetWhitelistingFilter(const std::string& url, |
546 ContentTypeMask contentTypeMask, | 568 ContentTypeMask contentTypeMask, |
547 const std::vector<std::string>& documentUrls) const; | 569 const std::vector<std::string>& documentUrls) const; |
548 }; | 570 }; |
549 } | 571 } |
550 | 572 |
551 #endif | 573 #endif |
OLD | NEW |