1.1 --- a/_locales/ar/messages.json
1.2 +++ b/_locales/ar/messages.json
1.3 @@ -13,19 +13,16 @@
1.4 "message": "إضافة الفلاتر؟"
1.5 },
1.6 "add_your_own_filters": {
1.7 "message": "أضف الفلاتر الخاصة بك"
1.8 },
1.9 "apply_changes": {
1.10 "message": "تطبيق التغييرات"
1.11 },
1.12 - "block_youtube": {
1.13 - "message": "منع الإعلانات داخل YouTube videos"
1.14 - },
1.15 "cancel": {
1.16 "description": "Cancel button label",
1.17 "message": "إلغاء"
1.18 },
1.19 "clickhide_instructions": {
1.20 "message": "بعد إغلاق هذه النافذة. إضغط (او إضغط بالزر اليمين) على أحد العناصر في الصفحة"
1.21 },
1.22 "description": {
2.1 --- a/_locales/de/messages.json
2.2 +++ b/_locales/de/messages.json
2.3 @@ -17,19 +17,16 @@
2.4 },
2.5 "apply_changes": {
2.6 "message": "Änderungen übernehmen"
2.7 },
2.8 "block_element": {
2.9 "description": "Name für Kontextmenü-Eintrag 'Element blocken'",
2.10 "message": "Element blocken"
2.11 },
2.12 - "block_youtube": {
2.13 - "message": "Blocke Werbung in YouTube Videos"
2.14 - },
2.15 "cancel": {
2.16 "description": "Name für den 'Abbrechen'-Button",
2.17 "message": "Abbrechen"
2.18 },
2.19 "clickhide_instructions": {
2.20 "message": "Nachdem dieses Fenster geschlossen ist, klicken Sie ein Element auf der Seite an, welches Sie blockieren wollen."
2.21 },
2.22 "description": {
3.1 --- a/_locales/en/messages.json
3.2 +++ b/_locales/en/messages.json
3.3 @@ -17,19 +17,16 @@
3.4 },
3.5 "apply_changes": {
3.6 "message": "Apply changes"
3.7 },
3.8 "block_element": {
3.9 "description": "Label for 'Block Element' context menu item",
3.10 "message": "Block Element"
3.11 },
3.12 - "block_youtube": {
3.13 - "message": "Block ads inside YouTube videos"
3.14 - },
3.15 "cancel": {
3.16 "description": "Cancel button label",
3.17 "message": "Cancel"
3.18 },
3.19 "clickhide_instructions": {
3.20 "message": "After closing this popup, click (or right-click) an element on the page."
3.21 },
3.22 "description": {
4.1 --- a/_locales/es/messages.json
4.2 +++ b/_locales/es/messages.json
4.3 @@ -17,19 +17,16 @@
4.4 },
4.5 "apply_changes": {
4.6 "message": "Aplicar cambios"
4.7 },
4.8 "block_element": {
4.9 "description": "Label for 'Block Element' context menu item",
4.10 "message": "Bloquear Elemento"
4.11 },
4.12 - "block_youtube": {
4.13 - "message": "Desactivar anuncios dentro de los vídeos de YouTube"
4.14 - },
4.15 "cancel": {
4.16 "description": "Cancel button label",
4.17 "message": "Cancelar"
4.18 },
4.19 "clickhide_instructions": {
4.20 "message": "Después de cerrar este menú desplegable, da clic (o clic derecho) en un elemento de la página."
4.21 },
4.22 "description": {
5.1 --- a/_locales/fi/messages.json
5.2 +++ b/_locales/fi/messages.json
5.3 @@ -13,19 +13,16 @@
5.4 "message": "Lisää suodatin?"
5.5 },
5.6 "add_your_own_filters": {
5.7 "message": "Lisää omia suodattimia"
5.8 },
5.9 "apply_changes": {
5.10 "message": "Hyväksy muutokset"
5.11 },
5.12 - "block_youtube": {
5.13 - "message": "Estä YouTube videoiden sisaiset mainokset (ei toimi Flash Block laajennuksen kanssa)"
5.14 - },
5.15 "cancel": {
5.16 "description": "Cancel button label",
5.17 "message": "Peruuta"
5.18 },
5.19 "clickhide_instructions": {
5.20 "message": "Tämän ikkunan sulkemisen jälkeen klikkaa (tai oikea klikkaa) elementtiä jonka haluat estää."
5.21 },
5.22 "description": {
6.1 --- a/_locales/fr/messages.json
6.2 +++ b/_locales/fr/messages.json
6.3 @@ -16,19 +16,16 @@
6.4 "message": "Ajouter vos propres filtres"
6.5 },
6.6 "apply_changes": {
6.7 "message": "Appliquer"
6.8 },
6.9 "block_element": {
6.10 "message": "Supprimer l'élément"
6.11 },
6.12 - "block_youtube": {
6.13 - "message": "Retirer les publicités dans les vidéos YouTube"
6.14 - },
6.15 "cancel": {
6.16 "description": "Cancel button label",
6.17 "message": "Annuler"
6.18 },
6.19 "clickhide_instructions": {
6.20 "message": "Après avoir clôturé ce popup, cliquez (ou droit-cliquez) un élément sur la page."
6.21 },
6.22 "description": {
7.1 --- a/_locales/he/messages.json
7.2 +++ b/_locales/he/messages.json
7.3 @@ -13,19 +13,16 @@
7.4 "message": "האם להוסיף מסנן/ים?"
7.5 },
7.6 "add_your_own_filters": {
7.7 "message": "הוספת מסננים משלך"
7.8 },
7.9 "apply_changes": {
7.10 "message": "החלת השינויים"
7.11 },
7.12 - "block_youtube": {
7.13 - "message": "חסימת פרסומות בתוך סרטוני YouTube"
7.14 - },
7.15 "cancel": {
7.16 "description": "Cancel button label",
7.17 "message": "ביטול"
7.18 },
7.19 "clickhide_instructions": {
7.20 "message": "לאחר סגירת חלונית זו, יש ללחוץ (לא משנה באיזה לחצן) על רכיב בעמוד זה."
7.21 },
7.22 "description": {
8.1 --- a/_locales/hu/messages.json
8.2 +++ b/_locales/hu/messages.json
8.3 @@ -13,19 +13,16 @@
8.4 "message": "Szűrő(k) hozzáadása?"
8.5 },
8.6 "add_your_own_filters": {
8.7 "message": "Add hozzá a saját szűrőidet"
8.8 },
8.9 "apply_changes": {
8.10 "message": "Változtatások alkalmazása"
8.11 },
8.12 - "block_youtube": {
8.13 - "message": "Youtube videókon belüli reklámok tiltása"
8.14 - },
8.15 "cancel": {
8.16 "description": "Cancel button label",
8.17 "message": "Mégse"
8.18 },
8.19 "clickhide_instructions": {
8.20 "message": "Miután bezártad ezt a buborékot, kattints egy elemre az oldalon.."
8.21 },
8.22 "description": {
9.1 --- a/_locales/it/messages.json
9.2 +++ b/_locales/it/messages.json
9.3 @@ -17,19 +17,16 @@
9.4 },
9.5 "apply_changes": {
9.6 "message": "Applica modifiche"
9.7 },
9.8 "block_element": {
9.9 "description": "Label for 'Block Element' context menu item",
9.10 "message": "Blocca Elemento"
9.11 },
9.12 - "block_youtube": {
9.13 - "message": "Blocca la pubblicità nei video YouTube"
9.14 - },
9.15 "cancel": {
9.16 "description": "Cancel button label",
9.17 "message": "Annulla"
9.18 },
9.19 "clickhide_instructions": {
9.20 "message": "Dopo la chiusura di questo pop-up, cliccate (o clic destro) su un elemento sulla pagina."
9.21 },
9.22 "description": {
10.1 --- a/_locales/ko/messages.json
10.2 +++ b/_locales/ko/messages.json
10.3 @@ -17,19 +17,16 @@
10.4 },
10.5 "apply_changes": {
10.6 "message": "적용"
10.7 },
10.8 "block_element": {
10.9 "description": "Label for 'Block Element' context menu item",
10.10 "message": "요소 숨김"
10.11 },
10.12 - "block_youtube": {
10.13 - "message": "유튜브 동영상 내부의 광고 차단"
10.14 - },
10.15 "cancel": {
10.16 "description": "Cancel button label",
10.17 "message": "취소"
10.18 },
10.19 "clickhide_instructions": {
10.20 "message": "이 팝업을 닫은 후 웹 페이지에서 HTML 요소를 클릭하십시오."
10.21 },
10.22 "description": {
11.1 --- a/_locales/lv/messages.json
11.2 +++ b/_locales/lv/messages.json
11.3 @@ -17,19 +17,16 @@
11.4 },
11.5 "apply_changes": {
11.6 "message": "Pielietot izmaiņas"
11.7 },
11.8 "block_element": {
11.9 "description": "Label for 'Block Element' context menu item",
11.10 "message": "Bloķēt elementu"
11.11 },
11.12 - "block_youtube": {
11.13 - "message": "Bloķēt reklāmas YouTube videoklipos"
11.14 - },
11.15 "cancel": {
11.16 "description": "Cancel button label",
11.17 "message": "Atcelt"
11.18 },
11.19 "clickhide_instructions": {
11.20 "message": "Pēc šī aizvēršanas, noklikšķiniet (vai noklikšķiniet ar labo pogu) uz elementa šajā lapā."
11.21 },
11.22 "description": {
12.1 --- a/_locales/pl/messages.json
12.2 +++ b/_locales/pl/messages.json
12.3 @@ -17,19 +17,16 @@
12.4 },
12.5 "apply_changes": {
12.6 "message": "Zakceptuj zmiany"
12.7 },
12.8 "block_element": {
12.9 "description": "Label for 'Block Element' context menu item",
12.10 "message": "Blokuj Element"
12.11 },
12.12 - "block_youtube": {
12.13 - "message": "Blokuj reklamy w filmach YouTube"
12.14 - },
12.15 "cancel": {
12.16 "description": "Cancel button label",
12.17 "message": "Anuluj"
12.18 },
12.19 "clickhide_instructions": {
12.20 "message": "Po zamknięciu tego okna, kliknij lewym (lub prawym) przyciskiem myszy w niechciany element na stronie."
12.21 },
12.22 "description": {
13.1 --- a/_locales/pt_BR/messages.json
13.2 +++ b/_locales/pt_BR/messages.json
13.3 @@ -17,19 +17,16 @@
13.4 },
13.5 "apply_changes": {
13.6 "message": "Aplicar mudanças"
13.7 },
13.8 "block_element": {
13.9 "description": "Legenda para o item 'Bloquear Elemento' no menu de contexto",
13.10 "message": "Bloquear Elemento"
13.11 },
13.12 - "block_youtube": {
13.13 - "message": "Bloquear propagandas dentro de vídeos do YouTube"
13.14 - },
13.15 "cancel": {
13.16 "description": "Rótulo para o botão de Cancelar",
13.17 "message": "Cancelar"
13.18 },
13.19 "clickhide_instructions": {
13.20 "message": "Após o encerramento desta janela, clique (ou dê um clique com o botão direito) em um elemento da página."
13.21 },
13.22 "description": {
14.1 --- a/_locales/ru/messages.json
14.2 +++ b/_locales/ru/messages.json
14.3 @@ -13,19 +13,16 @@
14.4 "message": "Добавить фильтр(ы)?"
14.5 },
14.6 "add_your_own_filters": {
14.7 "message": "Добавить свои фильтры"
14.8 },
14.9 "apply_changes": {
14.10 "message": "Применить изменения"
14.11 },
14.12 - "block_youtube": {
14.13 - "message": "Блокировать рекламу в видео на YouTube"
14.14 - },
14.15 "cancel": {
14.16 "description": "Cancel button label",
14.17 "message": "Отменить"
14.18 },
14.19 "clickhide_instructions": {
14.20 "message": "После закрытия этого всплывающего окна щёлкните левой или правой кнопкой мыши элемент на странице."
14.21 },
14.22 "description": {
15.1 --- a/_locales/sv/messages.json
15.2 +++ b/_locales/sv/messages.json
15.3 @@ -17,19 +17,16 @@
15.4 },
15.5 "apply_changes": {
15.6 "message": "Verkställ ändringar"
15.7 },
15.8 "block_element": {
15.9 "description": "Label for 'Block Element' context menu item",
15.10 "message": "Blockera objekt"
15.11 },
15.12 - "block_youtube": {
15.13 - "message": "Blockera annonser i YouTube-videos"
15.14 - },
15.15 "cancel": {
15.16 "description": "Cancel button label",
15.17 "message": "Avbryt"
15.18 },
15.19 "clickhide_instructions": {
15.20 "message": "När denna popup stängs, klicka (eller högerklicka) ett objekt på sidan."
15.21 },
15.22 "description": {
16.1 --- a/_locales/vi/messages.json
16.2 +++ b/_locales/vi/messages.json
16.3 @@ -17,19 +17,16 @@
16.4 },
16.5 "apply_changes": {
16.6 "message": "Lưu các thay đổi"
16.7 },
16.8 "block_element": {
16.9 "description": "Label for 'Block Element' context menu item",
16.10 "message": "Khóa đối tượng này"
16.11 },
16.12 - "block_youtube": {
16.13 - "message": "Chặn quảng cáo trong các đọan phim trên YouTube"
16.14 - },
16.15 "cancel": {
16.16 "description": "Cancel button label",
16.17 "message": "Hủy bỏ"
16.18 },
16.19 "clickhide_instructions": {
16.20 "message": "Sau khi đóng của sổ này, hãy ấn chuột (hoặc ấn chuột phải) vào đối tượng mà bạn cần khóa trên trang."
16.21 },
16.22 "description": {
17.1 --- a/_locales/zh_CN/messages.json
17.2 +++ b/_locales/zh_CN/messages.json
17.3 @@ -17,19 +17,16 @@
17.4 },
17.5 "apply_changes": {
17.6 "message": "应用"
17.7 },
17.8 "block_element": {
17.9 "description": "Label for 'Block Element' context menu item",
17.10 "message": "屏蔽此元素"
17.11 },
17.12 - "block_youtube": {
17.13 - "message": "屏蔽YouTube视频中的广告"
17.14 - },
17.15 "cancel": {
17.16 "description": "Cancel button label",
17.17 "message": "取消"
17.18 },
17.19 "clickhide_instructions": {
17.20 "message": "请点击(或右击)要屏蔽的元素"
17.21 },
17.22 "description": {
18.1 --- a/_locales/zh_TW/messages.json
18.2 +++ b/_locales/zh_TW/messages.json
18.3 @@ -17,19 +17,16 @@
18.4 },
18.5 "apply_changes": {
18.6 "message": "套用"
18.7 },
18.8 "block_element": {
18.9 "description": "Label for 'Block Element' context menu item",
18.10 "message": "阻擋網頁元素"
18.11 },
18.12 - "block_youtube": {
18.13 - "message": "阻擋 YouTube 影片廣告"
18.14 - },
18.15 "cancel": {
18.16 "description": "Cancel button label",
18.17 "message": "取消"
18.18 },
18.19 "clickhide_instructions": {
18.20 "message": "此彈出訊息關閉後,請點選網頁上的元素。"
18.21 },
18.22 "description": {
19.1 --- a/background.html
19.2 +++ b/background.html
19.3 @@ -8,18 +8,23 @@
19.4 <script type="text/javascript" src="modules/FilterStorage.jsm"></script>
19.5 <script type="text/javascript" src="modules/ElemHide.jsm"></script>
19.6 <script type="text/javascript" src="modules/FilterListener.jsm"></script>
19.7 <script type="text/javascript" src="modules/Matcher.jsm"></script>
19.8 <script type="text/javascript" src="modules/Synchronizer.jsm"></script>
19.9 <script type="text/javascript" src="lib/punycode.js"></script>
19.10 <script type="text/javascript" src="lib/publicSuffixList.js"></script>
19.11 <script type="text/javascript" src="lib/basedomain.js"></script>
19.12 +<script type="text/javascript" src="webrequest.js"></script>
19.13
19.14 <script type="text/javascript">
19.15 +// Some types cannot be distinguished
19.16 +RegExpFilter.typeMap.OBJECT_SUBREQUEST = RegExpFilter.typeMap.OBJECT;
19.17 +RegExpFilter.typeMap.MEDIA = RegExpFilter.typeMap.FONT = RegExpFilter.typeMap.OTHER;
19.18 +
19.19 FilterListener.startup();
19.20 Synchronizer.startup();
19.21
19.22 window.addEventListener("unload", FilterListener.shutdown);
19.23
19.24 FilterNotifier.addListener(function(action)
19.25 {
19.26 if (action == "load")
19.27 @@ -120,21 +125,22 @@ function setDefaultOptions()
19.28 {
19.29 if(!(opt in localStorage))
19.30 localStorage[opt] = val;
19.31 }
19.32
19.33 defaultOptionValue("shouldShowIcon", "true");
19.34 defaultOptionValue("shouldShowBlockElementMenu", "true");
19.35 defaultOptionValue("disableInlineTextAds", "true");
19.36 - defaultOptionValue("specialCaseYouTube", "true");
19.37
19.38 // If user had older version installed, get rid of old option
19.39 - if("experimental" in localStorage)
19.40 - delete localStorage["experimental"];
19.41 + if ("specialCaseYouTube" in localStorage)
19.42 + delete localStorage.specialCaseYouTube;
19.43 + if ("experimental" in localStorage)
19.44 + delete localStorage.experimental;
19.45 }
19.46
19.47 // Upgrade options before we do anything else.
19.48 setDefaultOptions();
19.49
19.50 /**
19.51 * Checks whether a page is whitelisted.
19.52 * @param url {String}
19.53 @@ -575,40 +581,12 @@ chrome.windows.getAll({populate: true},
19.54
19.55 // Update icon if a tab changes location
19.56 chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab)
19.57 {
19.58 chrome.tabs.sendRequest(tabId, {reqtype: "clickhide-deactivate"})
19.59 if(changeInfo.status == "loading")
19.60 refreshIconAndContextMenu(tab);
19.61 });
19.62 -
19.63 -// Only load webrequest.js if we have the necessary permissions
19.64 -var hasExperimentalPermissions = false;
19.65 -var webRequest = null;
19.66 -try
19.67 -{
19.68 - var dummy = function(){};
19.69 - webRequest = "webRequest" in chrome ? chrome.webRequest : chrome.experimental.webRequest;
19.70 - webRequest.onBeforeRequest.addListener(dummy, {urls: []});
19.71 - webRequest.onBeforeRequest.removeListener(dummy);
19.72 - hasExperimentalPermissions = true;
19.73 -
19.74 - // Some types cannot be distinguished
19.75 - RegExpFilter.typeMap.OBJECT_SUBREQUEST = RegExpFilter.typeMap.OBJECT;
19.76 - RegExpFilter.typeMap.MEDIA = RegExpFilter.typeMap.FONT = RegExpFilter.typeMap.OTHER;
19.77 -}
19.78 -catch (e) {}
19.79 -
19.80 -if (hasExperimentalPermissions)
19.81 -{
19.82 - window.addEventListener("load", function()
19.83 - {
19.84 - var script = document.createElement("script");
19.85 - script.setAttribute("src", "webrequest.js");
19.86 - document.body.appendChild(script);
19.87 - }, false);
19.88 -}
19.89 -
19.90 </script>
19.91
19.92 </head>
19.93 </html>
20.1 --- a/build.py
20.2 +++ b/build.py
20.3 @@ -26,39 +26,28 @@ Options:
20.4
20.5 def removeUpdateURL(zip, dir, fileName, fileData):
20.6 if fileName == 'manifest.json':
20.7 data = json.loads(fileData)
20.8 del data['update_url']
20.9 return json.dumps(data, sort_keys=True, indent=2)
20.10 return fileData
20.11
20.12 -def useExperimentalUpdateURL(zip, dir, fileName, fileData):
20.13 +def setExperimentalSettings(zip, dir, fileName, fileData):
20.14 if fileName == 'manifest.json':
20.15 data = json.loads(fileData)
20.16 if 'update_url' in data:
20.17 index = data['update_url'].rfind('/')
20.18 if index >= 0:
20.19 data['update_url'] = data['update_url'][0:index] + '-experimental' + data['update_url'][index:]
20.20 + data['permissions'] += ['experimental']
20.21 data['name'] += ' experimental build'
20.22 return json.dumps(data, sort_keys=True, indent=2)
20.23 return fileData
20.24
20.25 -def removeExperimentalPermissions(zip, dir, fileName, fileData):
20.26 - if fileName == 'manifest.json':
20.27 - data = json.loads(fileData)
20.28 - data['permissions'] = filter(lambda p: p != 'experimental', data['permissions'])
20.29 - if 'content_scripts' in data:
20.30 - for script in data['content_scripts']:
20.31 - if 'js' in script:
20.32 - script['js'] = filter(lambda s: s != 'include.experimental.js', script['js'])
20.33 -
20.34 - return json.dumps(data, sort_keys=True, indent=2)
20.35 - return fileData
20.36 -
20.37 def addBuildNumber(revision, zip, dir, fileName, fileData):
20.38 if fileName == 'manifest.json':
20.39 if not revision:
20.40 revision, dummy = subprocess.Popen(['hg', '-R', dir, 'id', '-n'], stdin=subprocess.PIPE, stdout=subprocess.PIPE).communicate()
20.41 revision = re.sub(r'\D', '', revision)
20.42 if len(revision) > 0:
20.43 data = json.loads(fileData)
20.44 while data['version'].count('.') < 2:
20.45 @@ -174,19 +163,17 @@ if __name__ == '__main__':
20.46 allowExperimental = True
20.47
20.48 filters = []
20.49 if isRelease:
20.50 filters.append(removeUpdateURL)
20.51 else:
20.52 filters.append(lambda zip, dir, fileName, fileData: addBuildNumber(buildNum, zip, dir, fileName, fileData))
20.53 if allowExperimental:
20.54 - filters.append(useExperimentalUpdateURL)
20.55 - if not allowExperimental:
20.56 - filters.append(removeExperimentalPermissions)
20.57 + filters.append(setExperimentalSettings)
20.58 filters.append(mergeContentScripts)
20.59
20.60 zipdata = packDirectory(inputdir, filters)
20.61 signature = None
20.62 pubkey = None
20.63 if keyfile != None:
20.64 signature = signBinary(zipdata, keyfile)
20.65 pubkey = getPublicKey(keyfile)
21.1 deleted file mode 100644
21.2 --- a/include.experimental.js
21.3 +++ /dev/null
21.4 @@ -1,8 +0,0 @@
21.5 -/*
21.6 - * This Source Code is subject to the terms of the Mozilla Public License
21.7 - * version 2.0 (the "License"). You can obtain a copy of the License at
21.8 - * http://mozilla.org/MPL/2.0/.
21.9 - */
21.10 -
21.11 -// This file will be included in experimental builds only, this is merely a flag
21.12 -var isExperimental = true;
22.1 --- a/include.postload.js
22.2 +++ b/include.postload.js
22.3 @@ -1,16 +1,14 @@
22.4 /*
22.5 * This Source Code is subject to the terms of the Mozilla Public License
22.6 * version 2.0 (the "License"). You can obtain a copy of the License at
22.7 * http://mozilla.org/MPL/2.0/.
22.8 */
22.9
22.10 -var enabled = false; // Enabled for this particular domain.
22.11 -
22.12 // Click-to-hide stuff
22.13 var clickHide_activated = false;
22.14 var currentElement = null;
22.15 var currentElement_boxShadow = null;
22.16 var currentElement_backgroundColor;
22.17 var clickHideFilters = null;
22.18 var highlightedElementsSelector = null;
22.19 var highlightedElementsBoxShadows = null;
22.20 @@ -348,16 +346,79 @@ function getElementURL(elt) {
22.21 url = params[0].getAttribute("value");
22.22 }
22.23 } else if(!url) {
22.24 url = elt.getAttribute("src") || elt.getAttribute("href");
22.25 }
22.26 return url;
22.27 }
22.28
22.29 +// Converts relative to absolute URL
22.30 +// e.g.: foo.swf on http://example.com/whatever/bar.html
22.31 +// -> http://example.com/whatever/foo.swf
22.32 +function relativeToAbsoluteUrl(url)
22.33 +{
22.34 + // If URL is already absolute, don't mess with it
22.35 + if (!url || /^[\w\-]+:/i.test(url))
22.36 + return url;
22.37 +
22.38 + // Leading / means absolute path
22.39 + if(url[0] == '/')
22.40 + return document.location.protocol + "//" + document.location.host + url;
22.41 +
22.42 + // Remove filename and add relative URL to it
22.43 + var base = document.baseURI.match(/.+\//);
22.44 + if(!base)
22.45 + return document.baseURI + "/" + url;
22.46 + return base[0] + url;
22.47 +}
22.48 +
22.49 +// This function Copyright (c) 2008 Jeni Tennison, from jquery.uri.js
22.50 +// and licensed under the MIT license. See jquery-*.min.js for details.
22.51 +function removeDotSegments(u) {
22.52 + var r = '', m = [];
22.53 + if (/\./.test(u)) {
22.54 + while (u !== undefined && u !== '') {
22.55 + if (u === '.' || u === '..') {
22.56 + u = '';
22.57 + } else if (/^\.\.\//.test(u)) { // starts with ../
22.58 + u = u.substring(3);
22.59 + } else if (/^\.\//.test(u)) { // starts with ./
22.60 + u = u.substring(2);
22.61 + } else if (/^\/\.(\/|$)/.test(u)) { // starts with /./ or consists of /.
22.62 + u = '/' + u.substring(3);
22.63 + } else if (/^\/\.\.(\/|$)/.test(u)) { // starts with /../ or consists of /..
22.64 + u = '/' + u.substring(4);
22.65 + r = r.replace(/\/?[^\/]+$/, '');
22.66 + } else {
22.67 + m = u.match(/^(\/?[^\/]*)(\/.*)?$/);
22.68 + u = m[2];
22.69 + r = r + m[1];
22.70 + }
22.71 + }
22.72 + return r;
22.73 + } else {
22.74 + return u;
22.75 + }
22.76 +}
22.77 +
22.78 +// Does some degree of URL normalization
22.79 +function normalizeURL(url)
22.80 +{
22.81 + var components = url.match(/(.+:\/\/.+?)\/(.*)/);
22.82 + if(!components)
22.83 + return url;
22.84 + var newPath = removeDotSegments(components[2]);
22.85 + if(newPath.length == 0)
22.86 + return components[1];
22.87 + if(newPath[0] != '/')
22.88 + newPath = '/' + newPath;
22.89 + return components[1] + newPath;
22.90 +}
22.91 +
22.92 // Content scripts are apparently invoked on non-HTML documents, so we have to
22.93 // check for that before doing stuff. |document instanceof HTMLDocument| check
22.94 // will fail on some sites like planet.mozilla.org because WebKit creates
22.95 // Document instances for XHTML documents, have to test the root element.
22.96 if (document.documentElement instanceof HTMLElement)
22.97 {
22.98 // Use a contextmenu handler to save the last element the user right-clicked on.
22.99 // To make things easier, we actually save the DOM event.
22.100 @@ -440,27 +501,27 @@ if (document.documentElement instanceof
22.101 case "clickhide-new-filter":
22.102 // The request is received by all frames, so ignore it if we're not the frame the
22.103 // user right-clicked in
22.104 if(!lastRightClickEvent)
22.105 return;
22.106 // We hope the URL we are given is the same as the one in the element referenced
22.107 // by lastRightClickEvent.target. If not, we just discard
22.108 var target = lastRightClickEvent.target;
22.109 - var url = relativeToAbsoluteUrl(target.src);
22.110 + var url = target.src;
22.111 // If we don't have the element with a src URL same as the filter, look for it.
22.112 // Chrome's context menu API is terrible. Why can't it give us the friggin' element
22.113 // to start with?
22.114 if(request.filter !== url) {
22.115 // Grab all elements with a src attribute.
22.116 // This won't work for all object/embed tags, but the context menu API doesn't
22.117 // work on those, so we're OK for now.
22.118 var elts = document.querySelectorAll('[src]');
22.119 for(var i=0; i<elts.length; i++) {
22.120 - url = relativeToAbsoluteUrl(elts[i].src);
22.121 + url = elts[i].src;
22.122 if(request.filter === url) {
22.123 // This is hopefully our element. In case of multiple elements
22.124 // with the same src, only one will be highlighted.
22.125 target = elts[i];
22.126 break;
22.127 }
22.128 }
22.129 }
22.130 @@ -481,24 +542,9 @@ if (document.documentElement instanceof
22.131 else
22.132 console.log("clickhide-new-filter: URLs don't match. Couldn't find that element.", request.filter, url, lastRightClickEvent.target.src);
22.133 break;
22.134 default:
22.135 sendResponse({});
22.136 break;
22.137 }
22.138 });
22.139 -
22.140 - if (isExperimental != true)
22.141 - {
22.142 - chrome.extension.sendRequest({reqtype: "get-domain-enabled-state"}, function(response)
22.143 - {
22.144 - enabled = response.enabled;
22.145 - if(enabled)
22.146 - {
22.147 - // Nuke background if it's an ad
22.148 - var bodyBackground = getComputedStyle(document.body).backgroundImage;
22.149 - if (bodyBackground && /^url\((.*)\)$/.test(bodyBackground) && shouldBlock(RegExp.$1, "IMAGE"))
22.150 - document.body.style.setProperty("background-image", "none", "important");
22.151 - }
22.152 - });
22.153 - }
22.154 }
23.1 --- a/include.preload.js
23.2 +++ b/include.preload.js
23.3 @@ -1,27 +1,14 @@
23.4 /*
23.5 * This Source Code is subject to the terms of the Mozilla Public License
23.6 * version 2.0 (the "License"). You can obtain a copy of the License at
23.7 * http://mozilla.org/MPL/2.0/.
23.8 */
23.9
23.10 -var isExperimental;
23.11 -
23.12 -var TagToType = {
23.13 - "SCRIPT": "SCRIPT",
23.14 - "IMG": "IMAGE",
23.15 - "LINK": "STYLESHEET",
23.16 - "OBJECT": "OBJECT",
23.17 - "EMBED": "OBJECT",
23.18 - "IFRAME": "SUBDOCUMENT"
23.19 -};
23.20 -
23.21 -var hostDomain = null;
23.22 -
23.23 var SELECTOR_GROUP_SIZE = 20;
23.24
23.25 var savedBeforeloadEvents = new Array();
23.26
23.27 var elemhideElt = null;
23.28
23.29 // Sets the currently used CSS rules for elemhide filters
23.30 function setElemhideCSSRules(selectors)
23.31 @@ -52,200 +39,25 @@ function setElemhideCSSRules(selectors)
23.32 {
23.33 var selector = selectors.slice(i, i + SELECTOR_GROUP_SIZE).join(", ");
23.34 elt.sheet.insertRule(selector + " { display: none !important; }", j);
23.35 }
23.36 }
23.37 setRules();
23.38 }
23.39
23.40 -// Hides a single element
23.41 -function nukeSingleElement(elt) {
23.42 - if(elt.innerHTML)
23.43 - elt.innerHTML = "";
23.44 - if(elt.innerText)
23.45 - elt.innerText = "";
23.46 - elt.style.display = "none";
23.47 - elt.style.visibility = "hidden";
23.48 - // If this is a LINK tag, it's probably a stylesheet, so disable it. Actually removing
23.49 - // it seems to intermittently break page rendering.
23.50 - if(elt.localName && elt.localName.toUpperCase() == "LINK")
23.51 - elt.setAttribute("disabled", "");
23.52 -}
23.53 -
23.54 -// This function Copyright (c) 2008 Jeni Tennison, from jquery.uri.js
23.55 -// and licensed under the MIT license. See jquery-*.min.js for details.
23.56 -function removeDotSegments(u) {
23.57 - var r = '', m = [];
23.58 - if (/\./.test(u)) {
23.59 - while (u !== undefined && u !== '') {
23.60 - if (u === '.' || u === '..') {
23.61 - u = '';
23.62 - } else if (/^\.\.\//.test(u)) { // starts with ../
23.63 - u = u.substring(3);
23.64 - } else if (/^\.\//.test(u)) { // starts with ./
23.65 - u = u.substring(2);
23.66 - } else if (/^\/\.(\/|$)/.test(u)) { // starts with /./ or consists of /.
23.67 - u = '/' + u.substring(3);
23.68 - } else if (/^\/\.\.(\/|$)/.test(u)) { // starts with /../ or consists of /..
23.69 - u = '/' + u.substring(4);
23.70 - r = r.replace(/\/?[^\/]+$/, '');
23.71 - } else {
23.72 - m = u.match(/^(\/?[^\/]*)(\/.*)?$/);
23.73 - u = m[2];
23.74 - r = r + m[1];
23.75 - }
23.76 - }
23.77 - return r;
23.78 - } else {
23.79 - return u;
23.80 - }
23.81 -}
23.82 -
23.83 -// Does some degree of URL normalization
23.84 -function normalizeURL(url)
23.85 -{
23.86 - var components = url.match(/(.+:\/\/.+?)\/(.*)/);
23.87 - if(!components)
23.88 - return url;
23.89 - var newPath = removeDotSegments(components[2]);
23.90 - if(newPath.length == 0)
23.91 - return components[1];
23.92 - if(newPath[0] != '/')
23.93 - newPath = '/' + newPath;
23.94 - return components[1] + newPath;
23.95 -}
23.96 -
23.97 -// Converts relative to absolute URL
23.98 -// e.g.: foo.swf on http://example.com/whatever/bar.html
23.99 -// -> http://example.com/whatever/foo.swf
23.100 -function relativeToAbsoluteUrl(url) {
23.101 - // If URL is already absolute, don't mess with it
23.102 - if(!url || url.match(/^http/i))
23.103 - return url;
23.104 - // Leading / means absolute path
23.105 - if(url[0] == '/') {
23.106 - return document.location.protocol + "//" + document.location.host + url;
23.107 - }
23.108 - // Remove filename and add relative URL to it
23.109 - var base = document.baseURI.match(/.+\//);
23.110 - if(!base)
23.111 - return document.baseURI + "/" + url;
23.112 - return base[0] + url;
23.113 -}
23.114 -
23.115 -// Extracts a domain name from a URL
23.116 -function extractDomainFromURL(url)
23.117 -{
23.118 - if(!url)
23.119 - return "";
23.120 -
23.121 - var x = url.substr(url.indexOf("://") + 3);
23.122 - x = x.substr(0, x.indexOf("/"));
23.123 - x = x.substr(x.indexOf("@") + 1);
23.124 - if (x.indexOf("[") == 0 && x.indexOf("]") > 0)
23.125 - {
23.126 - x = x.substring(1,x.indexOf("]"));
23.127 - }
23.128 - else
23.129 - {
23.130 - colPos = x.indexOf(":");
23.131 - if (colPos >= 0)
23.132 - x = x.substr(0, colPos);
23.133 - }
23.134 - return x;
23.135 -}
23.136 -
23.137 -/**
23.138 - * Checks whether a request is third party for the current document, uses
23.139 - * our effective document domain as received by the background process.
23.140 - */
23.141 -function isThirdParty(requestHost)
23.142 -{
23.143 - if (!hostDomain)
23.144 - return true;
23.145 -
23.146 - // Remove trailing dots
23.147 - requestHost = requestHost.replace(/\.+$/, "");
23.148 -
23.149 - if (requestHost.length > hostDomain.length)
23.150 - return (requestHost.substr(requestHost.length - hostDomain.length - 1) != "." + hostDomain);
23.151 - else
23.152 - return (requestHost != hostDomain);
23.153 -}
23.154 -
23.155 -// This beforeload handler is used before we hear back from the background process about
23.156 -// whether we're enabled etc. It saves the events so we can replay them to the normal
23.157 -// beforeload handler once we know whether we're enabled - to catch ads that might have
23.158 -// snuck by.
23.159 -function saveBeforeloadEvent(e) {
23.160 - savedBeforeloadEvents.push(e);
23.161 -}
23.162 -
23.163 -/**
23.164 - * Tests whether a request needs to be blocked.
23.165 - */
23.166 -function shouldBlock(/**String*/ url, /**String*/ type)
23.167 -{
23.168 - var url = relativeToAbsoluteUrl(url);
23.169 - var requestHost = extractDomainFromURL(url);
23.170 - var thirdParty = isThirdParty(requestHost);
23.171 - var match = defaultMatcher.matchesAny(url, type, window.location.hostname, thirdParty);
23.172 - return (match && match instanceof BlockingFilter);
23.173 -}
23.174 -
23.175 -/**
23.176 - * Responds to beforeload events by preventing load and nuking the element if
23.177 - * it's an ad.
23.178 - */
23.179 -function beforeloadHandler(/**Event*/ e)
23.180 -{
23.181 - if (shouldBlock(e.url, TagToType[e.target.localName.toUpperCase()]))
23.182 - {
23.183 - e.preventDefault();
23.184 - if (e.target)
23.185 - nukeSingleElement(e.target);
23.186 - }
23.187 -}
23.188 -
23.189 function sendRequests()
23.190 {
23.191 // Make sure this is really an HTML page, as Chrome runs these scripts on just about everything
23.192 if (!(document.documentElement instanceof HTMLElement))
23.193 return;
23.194
23.195 - // Blocking from content script is unnecessary in experimental builds, it is
23.196 - // done though webRequest API.
23.197 - if (isExperimental != true)
23.198 - {
23.199 - chrome.extension.sendRequest({reqtype: "get-settings", matcher: true, host: window.location.hostname}, function(response)
23.200 - {
23.201 - document.removeEventListener("beforeload", saveBeforeloadEvent, true);
23.202 -
23.203 - if (response.enabled)
23.204 - {
23.205 - hostDomain = response.hostDomain;
23.206 - defaultMatcher.fromCache(JSON.parse(response.matcherData));
23.207 -
23.208 - document.addEventListener("beforeload", beforeloadHandler, true);
23.209 -
23.210 - // Replay the events that were saved while we were waiting to learn whether we are enabled
23.211 - for(var i = 0; i < savedBeforeloadEvents.length; i++)
23.212 - beforeloadHandler(savedBeforeloadEvents[i]);
23.213 - }
23.214 - delete savedBeforeloadEvents;
23.215 - });
23.216 - }
23.217 -
23.218 chrome.extension.sendRequest({reqtype: "get-settings", selectors: true, host: window.location.hostname}, function(response)
23.219 {
23.220 setElemhideCSSRules(response.selectors);
23.221 });
23.222 }
23.223
23.224 -if (isExperimental != true)
23.225 - document.addEventListener("beforeload", saveBeforeloadEvent, true);
23.226 -
23.227 // In Chrome 18 the document might not be initialized yet
23.228 if (document.documentElement)
23.229 sendRequests();
23.230 else
23.231 window.setTimeout(sendRequests, 0);
24.1 deleted file mode 100644
24.2 --- a/include.youtube.js
24.3 +++ /dev/null
24.4 @@ -1,74 +0,0 @@
24.5 -/*
24.6 - * This Source Code is subject to the terms of the Mozilla Public License
24.7 - * version 2.0 (the "License"). You can obtain a copy of the License at
24.8 - * http://mozilla.org/MPL/2.0/.
24.9 - */
24.10 -
24.11 -var isExperimental;
24.12 -
24.13 -// Don't do anything in experimental builds, webRequest API will handle this
24.14 -if (isExperimental == true)
24.15 - return;
24.16 -
24.17 -// YouTube special case option
24.18 -var shouldRemoveYouTubeAds = undefined;
24.19 -var savedPlayer, savedPlayerVars;
24.20 -
24.21 -function stripYouTubePlayerAds(player, newVars)
24.22 -{
24.23 - player.setAttribute("flashvars", newVars.join("&"));
24.24 -
24.25 - // Remove the node and insert it back, the variables won't be reloaded otherwise
24.26 - var parent = player.parentNode;
24.27 - var insertBefore = player.nextSibling;
24.28 - parent.removeChild(player);
24.29 - parent.insertBefore(player, insertBefore);
24.30 -}
24.31 -
24.32 -function onYouTubeBeforeLoad(e)
24.33 -{
24.34 - var eltDomain = extractDomainFromURL(e.url);
24.35 - var player = e.target;
24.36 - if (player && /\bytimg\.com$/.test(eltDomain) &&
24.37 - /^(embed|object)$/.test(player.localName) &&
24.38 - player.hasAttribute("flashvars"))
24.39 - {
24.40 - // Remove a bunch of known parameters from flashvars attribute of the player
24.41 - var flashVars = player.getAttribute("flashvars").split("&");
24.42 - var newVars = [];
24.43 - for (var i = 0; i < flashVars.length; i++)
24.44 - if (!/^(ad\d*_|instream|infringe|invideo|interstitial|mpu|prerolls|tpas_ad_type_id|trueview|watermark)/.test(flashVars[i]))
24.45 - newVars.push(flashVars[i]);
24.46 - if (newVars.length != flashVars.length)
24.47 - {
24.48 - if (typeof shouldRemoveYouTubeAds == "undefined")
24.49 - {
24.50 - // If enabled-check hasn't returned yet, save the event and new flashvars so that ad removal
24.51 - // can occur when it does return. We ought only to need to save one since there will only be
24.52 - // one video player loaded at a time.
24.53 - savedPlayer = player;
24.54 - savedPlayerVars = newVars;
24.55 - }
24.56 - else if (shouldRemoveYouTubeAds)
24.57 - stripYouTubePlayerAds(player, newVars);
24.58 - }
24.59 - }
24.60 -}
24.61 -
24.62 -// Ask the backend whether we are enabled. This may take a while, so we attach the beforeload listener
24.63 -// no matter what. If this callback hasn't been invoked by the time we need to act on the YouTube
24.64 -// player object, the beforeload handler will just save the target object and new flashvars so that
24.65 -// this callback can modify the flashvars and reload the object.
24.66 -chrome.extension.sendRequest({reqtype: "get-domain-enabled-state"}, function(response)
24.67 -{
24.68 - shouldRemoveYouTubeAds = (response.enabled && response.specialCaseYouTube);
24.69 - if (shouldRemoveYouTubeAds)
24.70 - {
24.71 - if (savedPlayer)
24.72 - stripYouTubePlayerAds(savedPlayer, savedPlayerVars);
24.73 - }
24.74 - else
24.75 - document.removeEventListener("beforeload", onYouTubeBeforeLoad, true);
24.76 -});
24.77 -
24.78 -document.addEventListener("beforeload", onYouTubeBeforeLoad, true);
25.1 --- a/manifest.json
25.2 +++ b/manifest.json
25.3 @@ -1,41 +1,35 @@
25.4 {
25.5 "name": "__MSG_name__",
25.6 "version": "1.2",
25.7 - "minimum_chrome_version": "16.0",
25.8 + "minimum_chrome_version": "17.0",
25.9 "description": "__MSG_description__",
25.10 "default_locale": "en",
25.11 "update_url": "https://adblockplus.org/devbuilds/adblockpluschrome/updates.xml",
25.12 "page_action": {
25.13 "default icon": "icons/abp-19.png",
25.14 "default title": "Adblock Plus",
25.15 "popup": "popup.html"
25.16 },
25.17 "icons": {
25.18 "16": "icons/abp-16.png",
25.19 "32": "icons/abp-32.png",
25.20 "48": "icons/abp-48.png",
25.21 "128": "icons/abp-128.png"
25.22 },
25.23 "permissions": [
25.24 - "tabs", "http://*/*", "https://*/*", "contextMenus", "webRequest", "webRequestBlocking", "unlimitedStorage", "experimental"
25.25 + "tabs", "http://*/*", "https://*/*", "contextMenus", "webRequest", "webRequestBlocking", "unlimitedStorage"
25.26 ],
25.27 "background_page": "background.html",
25.28 "options_page": "options.html",
25.29 "content_scripts": [
25.30 {
25.31 - "matches": ["http://*.youtube.com/*", "https://*.youtube.com/*", "http://*.youtube-nocookie.com/*", "https://*.youtube-nocookie.com/*"],
25.32 - "js": ["include.experimental.js", "include.youtube.js"],
25.33 - "run_at": "document_start",
25.34 - "all_frames": true
25.35 - },
25.36 - {
25.37 "matches": ["http://*/*", "https://*/*"],
25.38 - "js": ["modules/patches.js", "modules/FilterClasses.jsm", "modules/Matcher.jsm", "include.experimental.js", "include.preload.js"],
25.39 + "js": ["include.preload.js"],
25.40 "run_at": "document_start",
25.41 "all_frames": true
25.42 },
25.43 {
25.44 "matches": ["http://*/*", "https://*/*"],
25.45 "js": ["include.postload.js", "include.textads.js"],
25.46 "run_at": "document_end",
25.47 "all_frames": true
26.1 --- a/options.html
26.2 +++ b/options.html
26.3 @@ -88,37 +88,31 @@ p {
26.4 }
26.5
26.6 </style>
26.7 <script type="text/javascript" charset="utf-8">
26.8
26.9 var backgroundPage = chrome.extension.getBackgroundPage();
26.10 var imports = ["FilterStorage", "FilterNotifier", "Subscription", "SpecialSubscription",
26.11 "DownloadableSubscription", "Filter", "WhitelistFilter",
26.12 - "Synchronizer", "Utils", "hasExperimentalPermissions"];
26.13 + "Synchronizer", "Utils"];
26.14 for (var i = 0; i < imports.length; i++)
26.15 window[imports[i]] = backgroundPage[imports[i]];
26.16
26.17 // Loads options from localStorage and sets UI elements accordingly
26.18 function loadOptions()
26.19 {
26.20 FilterNotifier.addListener(onFilterChange);
26.21
26.22 // Popuplate option checkboxes
26.23 initCheckbox("shouldShowIcon");
26.24 initCheckbox("shouldShowBlockElementMenu");
26.25 initCheckbox("disableInlineTextAds");
26.26 - initCheckbox("specialCaseYouTube");
26.27 initCheckbox("hidePlaceholders");
26.28
26.29 - if (hasExperimentalPermissions)
26.30 - document.getElementById("specialCaseYouTubeContainer").style.display = "none";
26.31 - else
26.32 - document.getElementById("hidePlaceholdersContainer").style.display = "none";
26.33 -
26.34 // Load recommended subscriptions
26.35 loadRecommendations();
26.36
26.37 // Show user's filters
26.38 reloadFilters();
26.39 }
26.40
26.41 // Reloads the displayed subscriptions and filters
26.42 @@ -765,19 +759,16 @@ document.title = chrome.i18n.getMessage(
26.43 <input type="checkbox" id="shouldShowIcon" /><span class="i18n_show_icon"></span>
26.44 </div>
26.45 <div id="shouldShowBlockElementMenuContainer">
26.46 <input type="checkbox" id="shouldShowBlockElementMenu" /><span class="i18n_show_block_element_menu"></span>
26.47 </div>
26.48 <div id="disableInlineTextAdsContainer">
26.49 <input type="checkbox" id="disableInlineTextAds" /><span class="i18n_disable_inline"></span>
26.50 </div>
26.51 - <div id="specialCaseYouTubeContainer">
26.52 - <input type="checkbox" id="specialCaseYouTube" /><span class="i18n_block_youtube"></span>
26.53 - </div>
26.54 <div id="hidePlaceholdersContainer">
26.55 <input type="checkbox" id="hidePlaceholders" /><span class="i18n_overlay_hideplaceholders_label"></span>
26.56 </div>
26.57 </p>
26.58
26.59 <p class="i18n_found_a_bug">"https://adblockplus.org/forum/viewforum.php?f=10"</p>
26.60 </div>
26.61 </div>
27.1 --- a/update_locales.py
27.2 +++ b/update_locales.py
27.3 @@ -117,16 +117,17 @@ if __name__ == '__main__':
27.4 'global_synchronize_invalid_data',
27.5 'global_synchronize_checksum_mismatch',
27.6 'settings_enabled_column',
27.7 'settings_remove_label',
27.8 'settings_addsubscription_label',
27.9 'subscriptionSelection_addSubscription_label',
27.10 'subscriptionSelection_other_label',
27.11 'hide_placeholders',
27.12 + 'block_youtube',
27.13 ]
27.14 imported = [
27.15 'global.properties subscription_invalid_location',
27.16 'global.properties remove_subscription_warning',
27.17 'overlay.dtd hideplaceholders.label',
27.18 'filters.dtd subscription.lastDownload.inProgress',
27.19 'filters.dtd subscription.lastDownload.invalidURL',
27.20 'filters.dtd subscription.lastDownload.connectionError',
28.1 --- a/webrequest.js
28.2 +++ b/webrequest.js
28.3 @@ -1,17 +1,17 @@
28.4 /*
28.5 * This Source Code is subject to the terms of the Mozilla Public License
28.6 * version 2.0 (the "License"). You can obtain a copy of the License at
28.7 * http://mozilla.org/MPL/2.0/.
28.8 */
28.9
28.10 -webRequest.onBeforeRequest.addListener(onBeforeRequest, {urls: ["http://*/*", "https://*/*"]}, ["blocking"]);
28.11 -webRequest.onBeforeSendHeaders.addListener(onBeforeSendHeaders, {urls: ["http://*/*", "https://*/*"]}, ["requestHeaders", "blocking"]);
28.12 -webRequest.onErrorOccurred.addListener(onErrorOccurred, {urls: ["http://*/*", "https://*/*"]});
28.13 +chrome.webRequest.onBeforeRequest.addListener(onBeforeRequest, {urls: ["http://*/*", "https://*/*"]}, ["blocking"]);
28.14 +chrome.webRequest.onBeforeSendHeaders.addListener(onBeforeSendHeaders, {urls: ["http://*/*", "https://*/*"]}, ["requestHeaders", "blocking"]);
28.15 +chrome.webRequest.onErrorOccurred.addListener(onErrorOccurred, {urls: ["http://*/*", "https://*/*"]});
28.16 chrome.tabs.onRemoved.addListener(forgetTab);
28.17
28.18 var frames = {};
28.19 var tabs = {};
28.20
28.21 function onBeforeRequest(details)
28.22 {
28.23 var type = details.type;