From fe9fefce310442da38b54e86ae4259f2a8bd93b9 Mon Sep 17 00:00:00 2001 From: gorhill Date: Fri, 10 Apr 2015 17:38:08 -0400 Subject: [PATCH 1/8] removed pointless lines -- remnants of trying out stuff --- platform/firefox/frameModule.js | 3 --- platform/firefox/vapi-background.js | 3 --- 2 files changed, 6 deletions(-) diff --git a/platform/firefox/frameModule.js b/platform/firefox/frameModule.js index 71ce100ca..f99d08977 100644 --- a/platform/firefox/frameModule.js +++ b/platform/firefox/frameModule.js @@ -168,9 +168,6 @@ const contentObserver = { type: type, url: location.spec }; - if ( type === 7 ) { - details.attrSrc = context.frameElement.getAttribute('src'); - } if ( typeof messageManager.sendRpcMessage === 'function' ) { // https://bugzil.la/1092216 diff --git a/platform/firefox/vapi-background.js b/platform/firefox/vapi-background.js index e4404bfa8..842ed3901 100644 --- a/platform/firefox/vapi-background.js +++ b/platform/firefox/vapi-background.js @@ -1311,9 +1311,6 @@ vAPI.net.registerListeners = function() { type: details.type, url: details.url }; - if ( details.attrSrc !== undefined ) { - lastRequest[0].attrSrc = details.attrSrc; - } }; vAPI.messaging.globalMessageManager.addMessageListener( From c90f085f58873c16df53ba35293dceb78218d801 Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 13 Apr 2015 15:51:55 -0400 Subject: [PATCH 2/8] this fixes https://github.com/chrisaljoudi/uBlock/issues/1251 --- src/js/uritools.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/uritools.js b/src/js/uritools.js index a79484b5d..fe905f301 100644 --- a/src/js/uritools.js +++ b/src/js/uritools.js @@ -49,7 +49,7 @@ var reRFC3986 = /^([^:\/?#]+:)?(\/\/[^\/?#]*)?([^?#]*)(\?[^#]*)?(#.*)?/; // Derived var reSchemeFromURI = /^[^:\/?#]+:/; var reAuthorityFromURI = /^(?:[^:\/?#]+:)?(\/\/[^\/?#]+)/; -var reCommonHostnameFromURL = /^https?:\/\/([0-9a-z_][0-9a-z._-]+)\//; +var reCommonHostnameFromURL = /^https?:\/\/([0-9a-z_][0-9a-z._-]*[0-9a-z])\//; // These are to parse authority field, not parsed by above official regex // IPv6 is seen as an exception: a non-compatible IPv6 is first tried, and From 57e0041629ba8f64a3564a2efef5f5c8870e1df4 Mon Sep 17 00:00:00 2001 From: gorhill Date: Thu, 16 Apr 2015 14:19:44 -0400 Subject: [PATCH 3/8] this fixes https://github.com/gorhill/uBlock/issues/63 --- src/js/messaging.js | 31 ++++++++++++++++++++----------- src/js/storage.js | 41 +++++++++++++++++++++++++++++++---------- 2 files changed, 51 insertions(+), 21 deletions(-) diff --git a/src/js/messaging.js b/src/js/messaging.js index 93151f731..595e8e5b2 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -974,16 +974,19 @@ var getLocalData = function(callback) { /******************************************************************************/ var backupUserData = function(callback) { - var onUserFiltersReady = function(details) { - var userData = { - timeStamp: Date.now(), - version: vAPI.app.version, - userSettings: µb.userSettings, - filterLists: µb.extractSelectedFilterLists(), - netWhitelist: µb.stringFromWhitelist(µb.netWhitelist), - dynamicFilteringString: µb.permanentFirewall.toString(), - userFilters: details.content - }; + var userData = { + timeStamp: Date.now(), + version: vAPI.app.version, + userSettings: µb.userSettings, + filterLists: {}, + netWhitelist: µb.stringFromWhitelist(µb.netWhitelist), + dynamicFilteringString: µb.permanentFirewall.toString(), + userFilters: '' + }; + + var onSelectedListsReady = function(filterLists) { + userData.filterLists = filterLists; + var now = new Date(); var filename = vAPI.i18n('aboutBackupFilename') .replace('{{datetime}}', now.toLocaleString()) @@ -1001,6 +1004,12 @@ var backupUserData = function(callback) { getLocalData(callback); }; + var onUserFiltersReady = function(details) { + userData.userFilters = details.content; + µb.extractSelectedFilterLists(onSelectedListsReady); + }; + + µb.assets.get('assets/user/filters.txt', onUserFiltersReady); }; @@ -1050,7 +1059,7 @@ var restoreUserData = function(request) { /******************************************************************************/ var resetUserData = function() { - vAPI.storage.clear(onAllRemoved); + vAPI.storage.clear(); // Keep global counts, people can become quite attached to numbers µb.saveLocalSettings(true); diff --git a/src/js/storage.js b/src/js/storage.js index 833e84149..cbc825077 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -102,19 +102,40 @@ // This will remove all unused filter list entries from // µBlock.remoteBlacklists`. This helps reduce the size of backup files. -µBlock.extractSelectedFilterLists = function() { - var r = JSON.parse(JSON.stringify(this.remoteBlacklists)); +µBlock.extractSelectedFilterLists = function(callback) { + var µb = this; - for ( var path in r ) { - if ( r.hasOwnProperty(path) === false ) { - continue; + var onBuiltinListsLoaded = function(details) { + var builtin; + try { + builtin = JSON.parse(details.content); + } catch (e) { + builtin = {}; } - if ( r[path].off !== false ) { - delete r[path]; - } - } - return r; + var result = JSON.parse(JSON.stringify(µb.remoteBlacklists)); + var builtinPath; + var defaultState; + + for ( var path in result ) { + if ( result.hasOwnProperty(path) === false ) { + continue; + } + builtinPath = path.replace(/^assets\/thirdparties\//, ''); + defaultState = builtin.hasOwnProperty(builtinPath) === false || + builtin[builtinPath].off === true; + if ( result[path].off === true && result[path].off === defaultState ) { + delete result[path]; + } + } + + callback(result); + }; + + // https://github.com/gorhill/uBlock/issues/63 + // Get built-in block lists: this will help us determine whether a + // specific list must be included in the result. + this.assets.get('assets/ublock/filter-lists.json', onBuiltinListsLoaded); }; /******************************************************************************/ From fdaee6f2a115769c8f9dbc39e32795b02977f80d Mon Sep 17 00:00:00 2001 From: gorhill Date: Thu, 23 Apr 2015 09:26:29 -0400 Subject: [PATCH 4/8] imported/patched Frisian translation work --- dist/description/description-fy.txt | 49 +++ src/_locales/fy/messages.json | 530 ++++++++++++++++++++++++++++ 2 files changed, 579 insertions(+) create mode 100644 dist/description/description-fy.txt create mode 100644 src/_locales/fy/messages.json diff --git a/dist/description/description-fy.txt b/dist/description/description-fy.txt new file mode 100644 index 000000000..eaedf1d09 --- /dev/null +++ b/dist/description/description-fy.txt @@ -0,0 +1,49 @@ +In effisjnte adblocker: brûkt hast gjin prosessorkrêft en ûnthâld. Dochs kin it tûzenen filters mear lade en tapasse as oare populêre adblockers. + +Yllustrearre effisjinsjefergeliking: https://github.com/chrisaljoudi/uBlock/wiki/uBlock-vs.-ABP:-efficiency-compared + +Gebrûk: Mei de grutte oan-útknop yn de pop-up kin uBlock permanint yn- of útskeakele wurde foar de aktuele website. It wurdt inkeld op de aktuele website tapast. It is gjin globale oan-útknop. + +*** + +Fleksibel, want it is mear as in "adblocker": it kin ek filters ynlêze en meitsje fan hostsbestannen. + +Standert wurde de folgjende filterlisten laden en tapast: + +- EasyList +- Peter Lowe’s Ad server list +- EasyPrivacy +- Malware domains + +Der binne mear listen beskikber dy't jo ynskeakelje kin: + +- Fanboy’s Enhanced Tracking List +- Dan Pollock’s hosts file +- hpHosts’s Ad and tracking servers +- MVPS HOSTS +- Spam404 +- En in protte mear + +Natuerlik wurdt it ûnthâldgebrûk grutter as der mear filters ynskeakele wurde. Mar sels nei it ynskeakeljen fan Fanboy's twa ekstra listen, hpHosts’s Ad en tracking servers, hat uBlock in leger ûnthâldgebrûk as oare populêre blockers. + +Let op, it gebrûk fan guon fan dizze ekstra listen ferheget de kâns dat websites net korrekt werjûn wurde -- seker de listen dy't normaal as hosts-bestân brûkt wurde. + +*** + +Sûnder de standert filterlist docht dizze add-on neat. Dus as jo ea echt in bydrage leverje wolle, tink dan oan de minsken dy't hurd wurkje om de filterlisten dy't jo brûke te ûnderhâlden, hokker allegearre fergees beskikber makke binne. + +*** + +Fergees. +Open source mei publike lisinsje (GPLv3) +Foar brûkers, troch brûkers. + +Meiwurkers @ Github: https://github.com/chrisaljoudi/uBlock/graphs/contributors +Meiwurkers @ Crowdin: https://crowdin.net/project/ublock + +*** + +Unthâld dat dit in hiele betide ferzje is as jo kommentaar leverje. + +Projektwizigingslochboek: +https://github.com/chrisaljoudi/uBlock/releases diff --git a/src/_locales/fy/messages.json b/src/_locales/fy/messages.json new file mode 100644 index 000000000..40fc59d86 --- /dev/null +++ b/src/_locales/fy/messages.json @@ -0,0 +1,530 @@ +{ + "extName":{ + "message":"uBlock", + "description":"extension name." + }, + "extShortDesc":{ + "message":"Einlik, in effisjinte adblocker. Brûkt hast gjin prosessorkrêft of ûnthâld.", + "description":"this will be in the chrome web store: must be 132 characters or less" + }, + "dashboardName":{ + "message":"uBlock — Dashboerd", + "description":"English: uBlock — Dashboard" + }, + "settingsPageName":{ + "message":"Ynstellingen", + "description":"appears as tab name in dashboard" + }, + "3pPageName":{ + "message":"Filters fan tredden", + "description":"appears as tab name in dashboard" + }, + "1pPageName":{ + "message":"Myn filters", + "description":"appears as tab name in dashboard" + }, + "rulesPageName":{ + "message":"Myn rigels", + "description":"appears as tab name in dashboard" + }, + "whitelistPageName":{ + "message":"Whitelist", + "description":"appears as tab name in dashboard" + }, + "statsPageName":{ + "message":"uBlock — Netwurkfersiken-lochboek", + "description":"Title for the network request log window" + }, + "aboutPageName":{ + "message":"Oer", + "description":"appears as tab name in dashboard" + }, + "popupPowerSwitchInfo":{ + "message":"Klikke: uBlock foar dizze website yn-\/útskeakelje.\n\nCtrl+klikke: uBlock inkeld foar dizze side útskeakelje.", + "description":"English: Click: disable\/enable uBlock for this site.\n\nCtrl+click: disable uBlock only on this page." + }, + "popupBlockedRequestPrompt":{ + "message":"blokkearre fersiken", + "description":"English: requests blocked" + }, + "popupBlockedOnThisPagePrompt":{ + "message":"op dizze side", + "description":"English: on this page" + }, + "popupBlockedStats":{ + "message":"{{count}} fan {{percent}}%", + "description":"Example: 15 or 13%" + }, + "popupBlockedSinceInstallPrompt":{ + "message":"sûnt ynstallaasje", + "description":"English: since install" + }, + "popupOr":{ + "message":"of", + "description":"English: or" + }, + "popupTipDashboard":{ + "message":"Klik om it dashboerd te iepenjen", + "description":"English: Click to open the dashboard" + }, + "popupTipPicker":{ + "message":"Elemintkiesmoadus iepenje", + "description":"English: Enter element picker mode" + }, + "popupTipLog":{ + "message":"Nei fersikenlochboek gean", + "description":"English: Go to request log" + }, + "popupTipNoPopups":{ + "message":"Alle pop-ups foar dizze website yn-\/útskeakelje", + "description":"English: Toggle the blocking of all popups for this site" + }, + "popupTipNoStrictBlocking":{ + "message":"Strikte blokkearring foar dizze website yn-\/útskeakelje", + "description":"English: Toggle strict blocking for this site" + }, + "popupAnyRulePrompt":{ + "message":"alles", + "description":"" + }, + "popupImageRulePrompt":{ + "message":"ôfbyldingen", + "description":"" + }, + "popup3pAnyRulePrompt":{ + "message":"fan tredden", + "description":"" + }, + "popupInlineScriptRulePrompt":{ + "message":"inline scripts", + "description":"" + }, + "popup1pScriptRulePrompt":{ + "message":"scripts fan de sites sels", + "description":"" + }, + "popup3pScriptRulePrompt":{ + "message":"scripts fan tredden", + "description":"" + }, + "popup3pFrameRulePrompt":{ + "message":"frames fan tredden", + "description":"" + }, + "popupHitDomainCountPrompt":{ + "message":"ferbûne domeinen", + "description":"appears in popup" + }, + "popupHitDomainCount":{ + "message":"{{count}} fan de {{total}}", + "description":"appears in popup" + }, + "pickerCreate":{ + "message":"Meitsje", + "description":"English: Create" + }, + "pickerPick":{ + "message":"Kieze", + "description":"English: Pick" + }, + "pickerQuit":{ + "message":"Slute", + "description":"English: Quit" + }, + "pickerNetFilters":{ + "message":"Netfilters", + "description":"English: Net filters" + }, + "pickerCosmeticFilters":{ + "message":"Kosmetyske filters", + "description":"English: Cosmetic filters" + }, + "pickerCosmeticFiltersHint":{ + "message":"Klik, Ctrl-klik", + "description":"English: Click, Ctrl-click" + }, + "pickerContextMenuEntry":{ + "message":"Elemint blokkearje", + "description":"English: Block element" + }, + "settingsCollapseBlockedPrompt":{ + "message":"Tydlike oantsjutting fan blokkearre eleminten ferstopje", + "description":"English: Hide placeholders of blocked elements" + }, + "settingsIconBadgePrompt":{ + "message":"It tal blokkearre oanfragen op it ikoan toane", + "description":"English: Show the number of blocked requests on the icon" + }, + "settingsContextMenuPrompt":{ + "message":"Wêr mooglik fan it kontekstmenu gebrûk meitsje", + "description":"English: Make use of context menu where appropriate" + }, + "settingsAdvancedUserPrompt":{ + "message":"Ik bin in betûfte brûker (Lês dit earst<\/a>)", + "description":"English: " + }, + "settingsExperimentalPrompt":{ + "message":"Eksperimentele funksjes ynskeakelje (Oer<\/a>)", + "description":"English: Enable experimental features" + }, + "settingsStorageUsed":{ + "message":"Brûkte ûnthâldromte: {{value}} bytes", + "description":"English: Storage used: {{}} bytes" + }, + "settingsLastRestorePrompt":{ + "message":"Lêste werstel:", + "description":"English: Last restore:" + }, + "settingsLastBackupPrompt":{ + "message":"Lêste reservekopy:", + "description":"English: Last backup:" + }, + "3pListsOfBlockedHostsPrompt":{ + "message":"{{netFilterCount}} netwurkfilters + {{cosmeticFilterCount}} kosmetyske filters fan:", + "description":"English: {{netFilterCount}} network filters + {{cosmeticFilterCount}} cosmetic filters from:" + }, + "3pListsOfBlockedHostsPerListStats":{ + "message":"{{used}} gebrûk fan {{total}}", + "description":"English: {{used}} used out of {{total}}" + }, + "3pAutoUpdatePrompt1":{ + "message":"Filterlisten automatysk fernije.", + "description":"English: Auto-update filter lists." + }, + "3pUpdateNow":{ + "message":"No fernije", + "description":"English: Update now" + }, + "3pPurgeAll":{ + "message":"Alle buffers leegje", + "description":"English: Purge all caches" + }, + "3pParseAllABPHideFiltersPrompt1":{ + "message":"Kosmetyske filters ynlêze en tapasse.", + "description":"English: Parse and enforce Adblock+ element hiding filters." + }, + "3pParseAllABPHideFiltersInfo":{ + "message":"

Dizze opsje skeakelt it ynlêzen en tapassen fan Adblock Plus-kompatibele “element hiding” filters<\/a> yn. Dizze filters binne suver kosmetysk: se ferstopje eleminten yn de webside dy't fisueel steurend wêze kinne en kinne dus net blokkearre wurde troch de op netfersiken basearre filterengine.<\/p>

It ynskeakeljen fan dizze funksje ferheget it ûnthâldgebrûk fan uBlock<\/i>.<\/p>", + "description":"English: see English messages.json" + }, + "3pListsOfBlockedHostsHeader":{ + "message":"List fan blokkearree hosts", + "description":"English: Lists of blocked hosts" + }, + "3pApplyChanges":{ + "message":"Wizigingen tapasse", + "description":"English: Apply changes" + }, + "3pGroupAds":{ + "message":"Advertinsjes", + "description":"English: Ads" + }, + "3pGroupPrivacy":{ + "message":"Privacy", + "description":"English: Privacy" + }, + "3pGroupMalware":{ + "message":"Malwaredomeinen", + "description":"English: Malware domains" + }, + "3pGroupSocial":{ + "message":"Sosjaal", + "description":"English: Social" + }, + "3pGroupMultipurpose":{ + "message":"Multifunksjoneel", + "description":"English: Multipurpose" + }, + "3pGroupRegions":{ + "message":"Gebieden, talen", + "description":"English: Regions, languages" + }, + "3pGroupCustom":{ + "message":"Oanpast", + "description":"English: Custom" + }, + "3pExternalListsHint":{ + "message":"Ien URL per rigel. Rigels begjinnend mei ‘!’ wurde negearre. Unjildige URL's wurde stil negearre.", + "description":"English: One URL per line. Lines prefixed with ‘!’ will be ignored. Invalid URLs will be silently ignored." + }, + "3pExternalListsApply":{ + "message":"Tapasse", + "description":"English: Parse" + }, + "3pExternalListPurge":{ + "message":"buffer leegje", + "description":"English: purge cache" + }, + "3pExternalListNew":{ + "message":"nije ferzje beskikber", + "description":"English: new version available" + }, + "3pExternalListObsolete":{ + "message":"ferâldere", + "description":"English: outdated" + }, + "3pLastUpdate":{ + "message":"Lêste fernijing: {{ago}}", + "description":"English: Last update: {{ago}}, where 'ago' will be replaced with something like '2 days ago'" + }, + "1pFormatHint":{ + "message":"Ien filter per rigel. Ien filter kin in gewoane hostnamme of ien Adblock Plus-kompatibel filter wêze. Rigels begjinnend mei ‘!’ wurde negearre.", + "description":"English: One filter per line. A filter can be a plain hostname, or an Adblock Plus-compatible filter. Lines prefixed with ‘!’ will be ignored." + }, + "1pImport":{ + "message":"Ymportearje en tafoegje", + "description":"English: Import and append" + }, + "1pExport":{ + "message":"Eksportearje", + "description":"English: Export" + }, + "1pExportFilename":{ + "message":"myn-ublock-statyske-filters_{{datetime}}.txt", + "description":"English: my-ublock-static-filters_{{datetime}}.txt" + }, + "1pApplyChanges":{ + "message":"Wizigingen tapasse", + "description":"English: Apply changes" + }, + "rulesPermanentHeader":{ + "message":"Permaninte rigels", + "description":"header" + }, + "rulesTemporaryHeader":{ + "message":"Tydlike rigels", + "description":"header" + }, + "rulesRevert":{ + "message":"Tebeksette", + "description":"This will remove all temporary rules" + }, + "rulesCommit":{ + "message":"Tapasse", + "description":"This will persist temporary rules" + }, + "rulesEdit":{ + "message":"Bewurkje", + "description":"Will enable manual-edit mode (textarea)" + }, + "rulesEditSave":{ + "message":"Bewarje", + "description":"Will save manually-edited content and exit manual-edit mode" + }, + "rulesEditDiscard":{ + "message":"Annulearje", + "description":"Will discard manually-edited content and exit manual-edit mode" + }, + "rulesImport":{ + "message":"Ut bestân ymportearje...", + "description":"" + }, + "rulesExport":{ + "message":"Nei bestân eksportearje", + "description":"" + }, + "rulesDefaultFileName":{ + "message":"myn-ublock-dynamyske-rigels_{{datetime}}.txt", + "description":"default file name to use" + }, + "rulesHint":{ + "message":"List fan jo dynamyske filterrigels.", + "description":"English: List of your dynamic filtering rules." + }, + "rulesFormatHint":{ + "message":"Rigelsyntaks: boarne bestimming type aksje<\/code> (folsleine dokumintaasje<\/a>).", + "description":"English: dynamic rule syntax and full documentation." + }, + "whitelistPrompt":{ + "message":"Jo list fan hostnammen wêrop uBlock útskeakele is. Ien per rigel ynjaan. Unjildige hostnammen wurde stil negearre.", + "description":"English: Your list of host names for which uBlock will be disabled. One host name per line. Invalid host names will be silently ignored." + }, + "whitelistImport":{ + "message":"Ymportearje en tafoegje", + "description":"English: Import and append" + }, + "whitelistExport":{ + "message":"Eksportearje", + "description":"English: Export" + }, + "whitelistExportFilename":{ + "message":"myn-ublock-whitelist_{{datetime}}.txt", + "description":"English: my-ublock-whitelist_{{datetime}}.txt" + }, + "whitelistApply":{ + "message":"Wizigingen tapasse", + "description":"English: Apply changes" + }, + "logNetRequestsPrompt":{ + "message":"It loggen fan netwurkfersiken ynskeakelje", + "description":"English: Enable the logging of network requests" + }, + "logNetRequestsHelp":{ + "message":"As jo wolle kinne jo de details fan netwurkfersiken besjen troch dizze opsje yn te skeakeljen. It loggen fan netwurkfersiken ferheget it ûnthâldgebrûk fan uBlock. Dizze opsje is standert útskeakele, omdat de measte brûkers dit net brûke.", + "description":"English: see _locales\/en\/messages.log" + }, + "logBlockedRequestsHeader":{ + "message":"Blokkearre fersiken", + "description":"English: Blocked requests" + }, + "logAllowedRequestsHeader":{ + "message":"Talitten fersiken", + "description":"English: Allowed requests" + }, + "logRequestsHeaderType":{ + "message":"Type", + "description":"English: Type" + }, + "logRequestsHeaderDomain":{ + "message":"Domein", + "description":"English: Domain" + }, + "logRequestsHeaderURL":{ + "message":"URL", + "description":"English: URL" + }, + "logRequestsHeaderFilter":{ + "message":"Filter", + "description":"English: Filter" + }, + "logBlockedRequestsEmpty":{ + "message":"Foar dizze side binne gjin blokkearre fersiken logd", + "description":"English: No blocked requests logged for this page" + }, + "logAllowedRequestsEmpty":{ + "message":"Faor dizze side binne gjin talitten fersiken logd", + "description":"English: No non-blocked requests logged for this page" + }, + "logBehindTheScene":{ + "message":"Achter de skermen", + "description":"Pretty name for behind-the-scene network requests" + }, + "logFilterPrompt":{ + "message":"lochboekitems filterje", + "description":"English: filter log entries" + }, + "logMaxEntriesTip":{ + "message":"Maksimum oantal lochboekitems", + "description":"Tooltip informaing that the input field is to set the maximum number of entries in the log" + }, + "aboutChangelog":{ + "message":"Wizigingenlochboek", + "description":"English: Change log" + }, + "aboutWiki":{ + "message":"Wiki", + "description":"English: project' wiki on Github" + }, + "aboutCode":{ + "message":"Boarnekoade (GPLv3)", + "description":"English: Source code (GPLv3)" + }, + "aboutContributors":{ + "message":"Meiwurkers", + "description":"English: Contributors" + }, + "aboutBackupDataButton":{ + "message":"Reservekopy nei bestân", + "description":"English: Backup to file" + }, + "aboutBackupFilename":{ + "message":"myn-ublock-reservekopy_{{datetime}}.txt", + "description":"English: my-ublock-backup_{{datetime}}.txt" + }, + "aboutRestoreDataButton":{ + "message":"Ut bestân werstelle...", + "description":"English: Restore from file..." + }, + "aboutResetDataButton":{ + "message":"Nei de standertynstellingen weromsette...", + "description":"English: Reset to default settings..." + }, + "aboutRestoreDataConfirm":{ + "message":"Al jo ynstellingen sille oerskreaun wurde mei de reservekopygegevens fan {{time}} en uBlock sil werstart wurde.\n\nBinne jo wis dat jo alle besteande ynstellingen oerskriuwe wolle mei de reservekopygegevens?", + "description":"Message asking user to confirm restore" + }, + "aboutRestoreDataError":{ + "message":"De gegevens koenen net lêzen wurde of binne ûnjildich", + "description":"Message to display when an error occurred during restore" + }, + "aboutResetDataConfirm":{ + "message":"Al jo ynstellingen sille fuortsmiten wurde, wêrnei uBlock werstart.\n\nYnstellingen dochs werstelle nei de standertwearden?", + "description":"Message asking user to confirm reset" + }, + "errorCantConnectTo":{ + "message":"Kin net ferbine mei {{url}}", + "description":"English: Network error: unable to connect to {{url}}" + }, + "subscriberConfirm":{ + "message":"uBlock: De folgjende URL oan jo oanpaste filterlisten tafoegje?\n\nTitel: \"{{title}}\"\nURL: {{url}}", + "description":"English: The message seen by the user to confirm subscription to a ABP filter list" + }, + "elapsedOneMinuteAgo":{ + "message":"in minút lyn", + "description":"English: a minute ago" + }, + "elapsedManyMinutesAgo":{ + "message":"{{value}} minuten lyn", + "description":"English: {{value}} minutes ago" + }, + "elapsedOneHourAgo":{ + "message":"in oere lyn", + "description":"English: an hour ago" + }, + "elapsedManyHoursAgo":{ + "message":"{{value}} oeren lyn", + "description":"English: {{value}} hours ago" + }, + "elapsedOneDayAgo":{ + "message":"in dei lyn", + "description":"English: a day ago" + }, + "elapsedManyDaysAgo":{ + "message":"{{value}} dagen lyn", + "description":"English: {{value}} days ago" + }, + "showDashboardButton":{ + "message":"Dashboard toane", + "description":"Firefox\/Fennec-specific: Show Dashboard" + }, + "showNetworkLogButton":{ + "message":"Netwurkfersiken-logboek toane", + "description":"Firefox\/Fennec-specific: Show Network Request Log" + }, + "fennecMenuItemBlockingOff":{ + "message":"útskeakele", + "description":"Firefox-specific: appears as 'uBlock (off)'" + }, + "docblockedPrompt1":{ + "message":"uBlock hat it laden fan de folgjende side opkeard:", + "description":"English: uBlock has prevented the following page from loading:" + }, + "docblockedPrompt2":{ + "message":"Fanwege it folgjende filter", + "description":"English: Because of the following filter" + }, + "docblockedBack":{ + "message":"Tebekgean", + "description":"English: Go back" + }, + "docblockedClose":{ + "message":"Finster slute", + "description":"English: Close this window" + }, + "docblockedProceed":{ + "message":"Strikte blokkearring útskeakelje foar {{hostname}}", + "description":"English: Disable strict blocking for {{hostname}} ..." + }, + "docblockedDisableTemporary":{ + "message":"Tydlik", + "description":"English: Temporarily" + }, + "docblockedDisablePermanent":{ + "message":"Permanint", + "description":"English: Permanently" + }, + "dummy":{ + "message":"This entry must be the last one", + "description":"so we dont need to deal with comma for last entry" + } +} From 7ad66fdb2c49f18152a7d67cba8898de73317342 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sun, 26 Apr 2015 18:48:21 -0400 Subject: [PATCH 5/8] this fixes https://github.com/gorhill/uBlock/issues/134 --- src/js/static-net-filtering.js | 24 +++++++++++++----------- src/js/storage.js | 18 +++++++++++------- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 9e2b4d588..8a860b14b 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -832,13 +832,21 @@ FilterHostnameDict.prototype.meltBucket = function(len, bucket) { } else { var offset = 0; while ( offset < bucket.length ) { - map[bucket.substring(offset, len)] = true; + map[bucket.substr(offset, len)] = true; offset += len; } } return map; }; +FilterHostnameDict.prototype.freezeBucket = function(bucket) { + var hostnames = Object.keys(bucket); + if ( hostnames[0].length * hostnames.length < this.cutoff ) { + return ' ' + hostnames.join(' ') + ' '; + } + return hostnames.sort().join(''); +}; + // How the key is derived dictates the number and size of buckets: // - more bits = more buckets = higher memory footprint // - less bits = less buckets = lower memory footprint @@ -886,7 +894,7 @@ FilterHostnameDict.prototype.add = function(hn) { return true; } if ( typeof bucket === 'string' ) { - bucket = this.dict[key] = this.meltBucket(hn.len, bucket); + bucket = this.dict[key] = this.meltBucket(hn.length, bucket); } if ( bucket.hasOwnProperty(hn) ) { return false; @@ -898,19 +906,13 @@ FilterHostnameDict.prototype.add = function(hn) { FilterHostnameDict.prototype.freeze = function() { var buckets = this.dict; - var bucket, hostnames, len; + var bucket; for ( var key in buckets ) { bucket = buckets[key]; if ( typeof bucket !== 'object' ) { continue; } - hostnames = Object.keys(bucket); - len = hostnames[0].length * hostnames.length; - if ( hostnames[0].length * hostnames.length < this.cutoff ) { - buckets[key] = ' ' + hostnames.join(' ') + ' '; - } else { - buckets[key] = hostnames.sort().join(''); - } + buckets[key] = this.freezeBucket(bucket); } }; @@ -923,7 +925,7 @@ FilterHostnameDict.prototype.matchesExactly = function(hn) { return false; } if ( typeof bucket === 'object' ) { - return bucket.hasOwnProperty(hn); + bucket = this.dict[key] = this.freezeBucket(bucket); } if ( bucket.charAt(0) === ' ' ) { return bucket.indexOf(' ' + hn + ' ') !== -1; diff --git a/src/js/storage.js b/src/js/storage.js index cbc825077..618761170 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -152,22 +152,26 @@ /******************************************************************************/ -µBlock.appendUserFilters = function(filter) { - if ( filter.length === 0 ) { +µBlock.appendUserFilters = function(filters) { + if ( filters.length === 0 ) { return; } var µb = this; - var onCompiledListLoaded = function(details) { + var onCompiledListLoaded = function() { + var compiledFilters = µb.compileFilters(filters); var snfe = µb.staticNetFilteringEngine; var cfe = µb.cosmeticFilteringEngine; var acceptedCount = snfe.acceptedCount + cfe.acceptedCount; var duplicateCount = snfe.duplicateCount + cfe.duplicateCount; - µb.applyCompiledFilters(details.content); + µb.applyCompiledFilters(compiledFilters); var entry = µb.remoteBlacklists[µb.userFiltersPath]; - entry.entryCount = snfe.acceptedCount + cfe.acceptedCount - acceptedCount; - entry.entryUsedCount = entry.entryCount - snfe.duplicateCount - cfe.duplicateCount + duplicateCount; + var deltaEntryCount = snfe.acceptedCount + cfe.acceptedCount - acceptedCount; + var deltaEntryUsedCount = deltaEntryCount - (snfe.duplicateCount + cfe.duplicateCount - duplicateCount); + entry.entryCount += deltaEntryCount; + entry.entryUsedCount += deltaEntryUsedCount; + vAPI.storage.set({ 'remoteBlacklists': µb.remoteBlacklists }); µb.staticNetFilteringEngine.freeze(); µb.cosmeticFilteringEngine.freeze(); }; @@ -187,7 +191,7 @@ // If we reached this point, the filter quite probably needs to be // added for sure: do not try to be too smart, trying to avoid // duplicates at this point may lead to more issues. - µb.saveUserFilters(details.content.trim() + '\n\n' + filter.trim(), onSaved); + µb.saveUserFilters(details.content.trim() + '\n\n' + filters.trim(), onSaved); }; this.loadUserFilters(onLoaded); From 032d5e0a343af49424f05d8d4900c2795ada6516 Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 27 Apr 2015 11:07:51 -0400 Subject: [PATCH 6/8] this fixes https://github.com/gorhill/uBlock/issues/135 --- src/js/background.js | 2 ++ src/js/start.js | 10 ++++++++++ src/js/tab.js | 8 +++++--- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/js/background.js b/src/js/background.js index e2a543a3f..d721c6e89 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -71,9 +71,11 @@ return { netWhitelist: {}, netWhitelistModifyTime: 0, netWhitelistDefault: [ + 'about-scheme', 'behind-the-scene', 'chrome-extension-scheme', 'chrome-scheme', + 'loopconversation.about-scheme', 'opera-scheme' ].join('\n').trim(), diff --git a/src/js/start.js b/src/js/start.js index bb7129683..b660644db 100644 --- a/src/js/start.js +++ b/src/js/start.js @@ -81,6 +81,16 @@ var onVersionReady = function(lastVersion) { ); µb.saveWhitelist(); } + // https://github.com/gorhill/uBlock/issues/135#issuecomment-96677379 + // `about:loopconversation` is used by Firefox for its Hello service + if ( lastVersion.localeCompare('0.9.3.5') <= 0 ) { + µb.netWhitelist = µb.whitelistFromString( + µb.stringFromWhitelist(µb.netWhitelist) + + '\n' + + 'loopconversation.about-scheme' + ); + µb.saveWhitelist(); + } if ( lastVersion !== vAPI.app.version ) { vAPI.storage.set({ version: vAPI.app.version }); } diff --git a/src/js/tab.js b/src/js/tab.js index e0ffe35f9..26c90038b 100644 --- a/src/js/tab.js +++ b/src/js/tab.js @@ -55,13 +55,15 @@ var µb = µBlock; return uri.normalizedURI(); } - var url = 'http://' + scheme + '-scheme/'; + var fakeHostname = scheme + '-scheme'; if ( uri.hostname !== '' ) { - url += uri.hostname + '/'; + fakeHostname = uri.hostname + '.' + fakeHostname; + } else if ( scheme === 'about' && uri.path !== '' ) { + fakeHostname = uri.path + '.' + fakeHostname; } - return url; + return 'http://' + fakeHostname + '/'; }; /******************************************************************************/ From e7626d7e1a10af377ac2e3e4ef0a65ae7471743b Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 27 Apr 2015 17:31:58 -0400 Subject: [PATCH 7/8] this fixes https://github.com/gorhill/uBlock/issues/142 --- src/js/background.js | 2 +- src/js/static-net-filtering.js | 45 ++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/js/background.js b/src/js/background.js index d721c6e89..7e9e900c6 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -88,7 +88,7 @@ return { // read-only systemSettings: { - compiledMagic: 'perhodsoahya', + compiledMagic: 'akjbdyreyxgm', selfieMagic: 'spqmeuaftfra' }, diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 8a860b14b..eec099fd5 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -571,6 +571,47 @@ FilterPlainHnAnchored.fromSelfie = function(s) { /******************************************************************************/ +// https://github.com/gorhill/uBlock/issues/142 + +var FilterPlainHnAnchoredHostname = function(s, hostname) { + this.s = s; + this.hostname = hostname; +}; + +FilterPlainHnAnchoredHostname.prototype.match = function(url, tokenBeg) { + if ( pageHostnameRegister.slice(-this.hostname.length) !== this.hostname ) { + return false; + } + if ( url.substr(tokenBeg, this.s.length) !== this.s ) { + return false; + } + // Valid only if hostname-valid characters to the left of token + var pos = url.indexOf('://'); + return pos !== -1 && + reURLPostHostnameAnchors.test(url.slice(pos + 3, tokenBeg)) === false; +}; + +FilterPlainHnAnchoredHostname.fid = FilterPlainHnAnchoredHostname.prototype.fid = '||ah'; + +FilterPlainHnAnchoredHostname.prototype.toString = function() { + return '||' + this.s; +}; + +FilterPlainHnAnchoredHostname.prototype.toSelfie = function() { + return this.s + '\t' + this.hostname; +}; + +FilterPlainHnAnchoredHostname.compile = function(details, hostname) { + return details.f + '\t' + hostname; +}; + +FilterPlainHnAnchoredHostname.fromSelfie = function(s) { + var pos = s.indexOf('\t'); + return new FilterPlainHnAnchoredHostname(s.slice(0, pos), s.slice(pos + 1)); +}; + +/******************************************************************************/ + // Generic filter var FilterGeneric = function(s, anchor) { @@ -1143,6 +1184,9 @@ var getHostnameBasedFilterClass = function(details) { if ( details.anchor > 0 ) { return FilterPlainRightAnchoredHostname; } + if ( details.hostnameAnchored ) { + return FilterPlainHnAnchoredHostname; + } if ( details.tokenBeg === 0 ) { return FilterPlainPrefix0Hostname; } @@ -1594,6 +1638,7 @@ FilterContainer.prototype.factories = { 'a|': FilterPlainRightAnchored, 'a|h': FilterPlainRightAnchoredHostname, '||a': FilterPlainHnAnchored, + '||ah': FilterPlainHnAnchoredHostname, '//': FilterRegex, '//h': FilterRegexHostname, '{h}': FilterHostnameDict, From 5205f1474c74bffd24ad8409b6817845a9b660d0 Mon Sep 17 00:00:00 2001 From: gorhill Date: Wed, 29 Apr 2015 12:21:46 -0400 Subject: [PATCH 8/8] important fixes: #150 + a case where a tab could still be unbound --- platform/firefox/vapi-background.js | 2 +- src/js/background.js | 2 +- src/js/tab.js | 4 +++ src/js/traffic.js | 6 +++- src/js/uritools.js | 11 ++++++-- src/lib/publicsuffixlist.js | 44 ++++++++++++++++++++++++++++- 6 files changed, 63 insertions(+), 6 deletions(-) diff --git a/platform/firefox/vapi-background.js b/platform/firefox/vapi-background.js index e061e227a..d6817b21e 100644 --- a/platform/firefox/vapi-background.js +++ b/platform/firefox/vapi-background.js @@ -2173,7 +2173,7 @@ vAPI.onLoadAllCompleted = function() { var tabId = this.tabs.getTabId(tab); var browser = getBrowserForTab(tab); - µb.tabContextManager.commit(tabId, browser.currentURI.asciiSpec); + µb.tabContextManager.commit(tabId); µb.bindTabToPageStats(tabId, browser.currentURI.asciiSpec); browser.messageManager.sendAsyncMessage( location.host + '-load-completed' diff --git a/src/js/background.js b/src/js/background.js index 7e9e900c6..0638be3c3 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -88,7 +88,7 @@ return { // read-only systemSettings: { - compiledMagic: 'akjbdyreyxgm', + compiledMagic: 'eopszukpnrct', selfieMagic: 'spqmeuaftfra' }, diff --git a/src/js/tab.js b/src/js/tab.js index 26c90038b..1dda0a6c5 100644 --- a/src/js/tab.js +++ b/src/js/tab.js @@ -209,6 +209,10 @@ housekeep itself. if ( vAPI.isBehindTheSceneTabId(this.tabId) ) { return; } + var count = this.stack.length; + if ( count !== 0 && this.stack[count - 1] === url ) { + return; + } this.stack.push(url); this.update(); }; diff --git a/src/js/traffic.js b/src/js/traffic.js index e38d49bde..41db50732 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -287,6 +287,9 @@ var onHeadersReceived = function(details) { return { 'responseHeaders': details.responseHeaders }; }; + +/******************************************************************************/ + var onRootFrameHeadersReceived = function(details) { var tabId = details.tabId; var requestURL = details.url; @@ -297,6 +300,8 @@ var onRootFrameHeadersReceived = function(details) { // ... if ( headerValue(details.responseHeaders, 'content-disposition').lastIndexOf('attachment', 0) === 0 ) { µb.tabContextManager.unpush(tabId, requestURL); + } else { + µb.tabContextManager.push(tabId, requestURL); } // Lookup the page store associated with this tab id. @@ -304,7 +309,6 @@ var onRootFrameHeadersReceived = function(details) { if ( !pageStore ) { pageStore = µb.bindTabToPageStats(tabId, 'beforeRequest'); } - // I can't think of how pageStore could be null at this point. var context = pageStore.createContextFromPage(); context.requestURL = requestURL + '{inline-script}'; diff --git a/src/js/uritools.js b/src/js/uritools.js index fe905f301..971c0c4b3 100644 --- a/src/js/uritools.js +++ b/src/js/uritools.js @@ -72,10 +72,10 @@ var reIPAddressNaive = /^\d+\.\d+\.\d+\.\d+$|^\[[\da-zA-Z:]+\]$/; // Accurate tests // Source.: http://stackoverflow.com/questions/5284147/validating-ipv4-addresses-with-regexp/5284410#5284410 -var reIPv4 = /^((25[0-5]|2[0-4]\d|[01]?\d\d?)(\.|$)){4}/; +//var reIPv4 = /^((25[0-5]|2[0-4]\d|[01]?\d\d?)(\.|$)){4}/; // Source: http://forums.intermapper.com/viewtopic.php?p=1096#1096 -var reIPv6 = /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/; +//var reIPv6 = /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/; /******************************************************************************/ @@ -366,11 +366,18 @@ var domainCachePrune = function() { } }; +var domainCacheReset = function() { + domainCache = {}; + domainCacheCount = 0; +}; + var domainCache = {}; var domainCacheCount = 0; var domainCacheCountLowWaterMark = 75; var domainCacheCountHighWaterMark = 100; +psl.onChanged.addListener(domainCacheReset); + /******************************************************************************/ URI.domainFromURI = function(uri) { diff --git a/src/lib/publicsuffixlist.js b/src/lib/publicsuffixlist.js index fa1803704..89749867c 100644 --- a/src/lib/publicsuffixlist.js +++ b/src/lib/publicsuffixlist.js @@ -33,6 +33,8 @@ ;(function(root) { +'use strict'; + /******************************************************************************/ var exceptions = {}; @@ -45,6 +47,8 @@ var selfieMagic = 'iscjsfsaolnm'; var cutoffLength = 256; var mustPunycode = /[^a-z0-9.-]/; +var onChangedListeners = []; + /******************************************************************************/ // In the context of this code, a domain is defined as: @@ -234,6 +238,7 @@ function parse(text, toAscii) { } crystallize(exceptions); crystallize(rules); + callListeners(onChangedListeners); } /******************************************************************************/ @@ -302,11 +307,47 @@ function fromSelfie(selfie) { } rules = selfie.rules; exceptions = selfie.exceptions; + callListeners(onChangedListeners); return true; } /******************************************************************************/ +var addListener = function(listeners, callback) { + if ( typeof callback !== 'function' ) { + return; + } + if ( listeners.indexOf(callback) === -1 ) { + listeners.push(callback); + } +}; + +var removeListener = function(listeners, callback) { + var pos = listeners.indexOf(callback); + if ( pos !== -1 ) { + listeners.splice(pos, 1); + } +}; + +var callListeners = function(listeners) { + for ( var i = 0; i < listeners.length; i++ ) { + listeners[i](); + } +}; + +/******************************************************************************/ + +var onChanged = { + addListener: function(callback) { + addListener(onChangedListeners, callback); + }, + removeListener: function(callback) { + removeListener(onChangedListeners, callback); + } +}; + +/******************************************************************************/ + // Public API root = root || window; @@ -317,7 +358,8 @@ root.publicSuffixList = { 'getDomain': getDomain, 'getPublicSuffix': getPublicSuffix, 'toSelfie': toSelfie, - 'fromSelfie': fromSelfie + 'fromSelfie': fromSelfie, + 'onChanged': onChanged }; /******************************************************************************/