diff --git a/platform/chromium/vapi-background.js b/platform/chromium/vapi-background.js index 1028f0808..b2737fdab 100644 --- a/platform/chromium/vapi-background.js +++ b/platform/chromium/vapi-background.js @@ -245,10 +245,6 @@ vAPI.tabs.registerListeners = function() { } }; - var onUpdated = function(tabId, changeInfo, tab) { - onUpdatedClient(tabId, changeInfo, tab); - }; - var onCommitted = function(details) { if ( details.frameId !== 0 ) { return; @@ -256,9 +252,18 @@ vAPI.tabs.registerListeners = function() { onNavigationClient(details); }; - chrome.webNavigation.onCreatedNavigationTarget.addListener(onCreatedNavigationTarget); + var onActivated = function(details) { + vAPI.contextMenu.onMustUpdate(details.tabId); + }; + + var onUpdated = function(tabId, changeInfo, tab) { + onUpdatedClient(tabId, changeInfo, tab); + }; + chrome.webNavigation.onBeforeNavigate.addListener(onBeforeNavigate); chrome.webNavigation.onCommitted.addListener(onCommitted); + chrome.webNavigation.onCreatedNavigationTarget.addListener(onCreatedNavigationTarget); + chrome.tabs.onActivated.addListener(onActivated); chrome.tabs.onUpdated.addListener(onUpdated); if ( typeof this.onClosed === 'function' ) { @@ -291,9 +296,7 @@ vAPI.tabs.get = function(tabId, callback) { var onTabReceived = function(tabs) { // https://code.google.com/p/chromium/issues/detail?id=410868#c8 - if ( chrome.runtime.lastError ) { - /* noop */ - } + void chrome.runtime.lastError; callback(tabs[0]); }; chrome.tabs.query({ active: true, currentWindow: true }, onTabReceived); @@ -553,6 +556,7 @@ vAPI.setIcon = function(tabId, iconStatus, badge) { { '19': 'img/browsericons/icon19-off.png', '38': 'img/browsericons/icon38-off.png' }; chrome.browserAction.setIcon({ tabId: tabId, path: iconPaths }, onIconReady); + vAPI.contextMenu.onMustUpdate(tabId); }; /******************************************************************************/ @@ -784,6 +788,51 @@ vAPI.net.registerListeners = function() { var µb = µBlock; var µburi = µb.URI; + // Chromium-based browsers understand only these network request types. + var validTypes = { + 'main_frame': true, + 'sub_frame': true, + 'stylesheet': true, + 'script': true, + 'image': true, + 'object': true, + 'xmlhttprequest': true, + 'other': true + }; + + var denormalizeTypes = function(aa) { + if ( aa.length === 0 ) { + return Object.keys(validTypes); + } + var out = []; + var i = aa.length, + type, + needOther = true; + while ( i-- ) { + type = aa[i]; + if ( validTypes.hasOwnProperty(type) ) { + out.push(type); + } + if ( type === 'other' ) { + needOther = false; + } + } + if ( needOther ) { + out.push('other'); + } + return out; + }; + + var headerValue = function(headers, name) { + var i = headers.length; + while ( i-- ) { + if ( headers[i].name.toLowerCase() === name ) { + return headers[i].value.trim(); + } + } + return ''; + }; + var normalizeRequestDetails = function(details) { µburi.set(details.url); @@ -801,34 +850,45 @@ vAPI.net.registerListeners = function() { // https://github.com/chrisaljoudi/uBlock/issues/862 // If no transposition possible, transpose to `object` as per // Chromium bug 410382 (see below) - if ( pos === -1 ) { - details.type = 'object'; - return; - } + if ( pos !== -1 ) { + var ext = tail.slice(pos) + '.'; + if ( '.eot.ttf.otf.svg.woff.woff2.'.indexOf(ext) !== -1 ) { + details.type = 'font'; + return; + } - var ext = tail.slice(pos) + '.'; - if ( '.eot.ttf.otf.svg.woff.woff2.'.indexOf(ext) !== -1 ) { - details.type = 'font'; - return; - } - // Still need this because often behind-the-scene requests are wrongly - // categorized as 'other' - if ( '.ico.png.gif.jpg.jpeg.webp.'.indexOf(ext) !== -1 ) { - details.type = 'image'; - return; - } - // https://code.google.com/p/chromium/issues/detail?id=410382 - details.type = 'object'; - }; + if ( '.mp3.mp4.webm.'.indexOf(ext) !== -1 ) { + details.type = 'media'; + return; + } - var headerValue = function(headers, name) { - var i = headers.length; - while ( i-- ) { - if ( headers[i].name.toLowerCase() === name ) { - return headers[i].value.trim(); + // Still need this because often behind-the-scene requests are wrongly + // categorized as 'other' + if ( '.ico.png.gif.jpg.jpeg.webp.'.indexOf(ext) !== -1 ) { + details.type = 'image'; + return; } } - return ''; + + // Try to extract type from response headers if present. + if ( details.responseHeaders ) { + var contentType = headerValue(details.responseHeaders, 'content-type'); + if ( contentType.startsWith('font/') ) { + details.type = 'font'; + return; + } + if ( contentType.startsWith('image/') ) { + details.type = 'image'; + return; + } + if ( contentType.startsWith('audio/') || contentType.startsWith('video/') ) { + details.type = 'media'; + return; + } + } + + // https://code.google.com/p/chromium/issues/detail?id=410382 + details.type = 'object'; }; var onBeforeRequestClient = this.onBeforeRequest.callback; @@ -839,13 +899,7 @@ vAPI.net.registerListeners = function() { var onHeadersReceivedClient = this.onHeadersReceived.callback; var onHeadersReceivedClientTypes = this.onHeadersReceived.types.slice(0); - var onHeadersReceivedTypes = onHeadersReceivedClientTypes.slice(0); - if ( - onHeadersReceivedTypes.length !== 0 && - onHeadersReceivedTypes.indexOf('other') === -1 - ) { - onHeadersReceivedTypes.push('other'); - } + var onHeadersReceivedTypes = denormalizeTypes(onHeadersReceivedClientTypes); var onHeadersReceived = function(details) { normalizeRequestDetails(details); // Hack to work around Chromium API limitations, where requests of @@ -857,21 +911,18 @@ vAPI.net.registerListeners = function() { // `other` always becomes `object` when it can't be normalized into // something else. Test case for "unfriendly" font URLs: // https://www.google.com/fonts - if ( details.type === 'object' ) { - if ( headerValue(details.responseHeaders, 'content-type').startsWith('font/') ) { - details.type = 'font'; - var r = onBeforeRequestClient(details); - if ( typeof r === 'object' && r.cancel === true ) { - return { cancel: true }; - } - } - if ( - onHeadersReceivedClientTypes.length !== 0 && - onHeadersReceivedClientTypes.indexOf(details.type) === -1 - ) { - return; + if ( details.type === 'font' ) { + var r = onBeforeRequestClient(details); + if ( typeof r === 'object' && r.cancel === true ) { + return { cancel: true }; } } + if ( + onHeadersReceivedClientTypes.length !== 0 && + onHeadersReceivedClientTypes.indexOf(details.type) === -1 + ) { + return; + } return onHeadersReceivedClient(details); }; @@ -921,15 +972,46 @@ vAPI.net.registerListeners = function() { /******************************************************************************/ vAPI.contextMenu = { - create: function(details, callback) { - this.menuId = details.id; - this.callback = callback; - chrome.contextMenus.create(details); - chrome.contextMenus.onClicked.addListener(this.callback); + _callback: null, + _entries: [], + _createEntry: function(entry) { + chrome.contextMenus.create(JSON.parse(JSON.stringify(entry)), function() { + void chrome.runtime.lastError; + }); }, - remove: function() { - chrome.contextMenus.onClicked.removeListener(this.callback); - chrome.contextMenus.remove(this.menuId); + onMustUpdate: function() {}, + setEntries: function(entries, callback) { + entries = entries || []; + var n = Math.max(this._entries.length, entries.length), + oldEntryId, newEntry; + for ( var i = 0; i < n; i++ ) { + oldEntryId = this._entries[i]; + newEntry = entries[i]; + if ( oldEntryId && newEntry ) { + if ( newEntry.id !== oldEntryId ) { + chrome.contextMenus.remove(oldEntryId); + this._createEntry(newEntry); + this._entries[i] = newEntry.id; + } + } else if ( oldEntryId && !newEntry ) { + chrome.contextMenus.remove(oldEntryId); + } else if ( !oldEntryId && newEntry ) { + this._createEntry(newEntry); + this._entries[i] = newEntry.id; + } + } + n = this._entries.length = entries.length; + callback = callback || null; + if ( callback === this._callback ) { + return; + } + if ( n !== 0 && callback !== null ) { + chrome.contextMenus.onClicked.addListener(callback); + this._callback = callback; + } else if ( n === 0 && this._callback !== null ) { + chrome.contextMenus.onClicked.removeListener(this._callback); + this._callback = null; + } } }; diff --git a/platform/chromium/vapi-client.js b/platform/chromium/vapi-client.js index 55f658f0c..6fdc851fc 100644 --- a/platform/chromium/vapi-client.js +++ b/platform/chromium/vapi-client.js @@ -19,6 +19,8 @@ Home: https://github.com/gorhill/uBlock */ +/* global HTMLDocument, XMLDocument */ + // For non background pages /******************************************************************************/ @@ -29,6 +31,18 @@ /******************************************************************************/ +// https://github.com/chrisaljoudi/uBlock/issues/464 +if ( document instanceof HTMLDocument === false ) { + // https://github.com/chrisaljoudi/uBlock/issues/1528 + // A XMLDocument can be a valid HTML document. + if ( + document instanceof XMLDocument === false || + document.createElement('div') instanceof HTMLDivElement === false + ) { + return; + } +} + // https://github.com/gorhill/uBlock/issues/1124 // Looks like `contentType` is on track to be standardized: // https://dom.spec.whatwg.org/#concept-document-content-type @@ -43,14 +57,12 @@ var chrome = self.chrome; // https://github.com/chrisaljoudi/uBlock/issues/456 // Already injected? -if ( vAPI.vapiClientInjected ) { - //console.debug('vapi-client.js already injected: skipping.'); +if ( vAPI.sessionId ) { return; } -vAPI.vapiClientInjected = true; vAPI.sessionId = String.fromCharCode(Date.now() % 26 + 97) + - Math.random().toString(36).slice(2); + Math.random().toString(36).slice(2); vAPI.chrome = true; /******************************************************************************/ @@ -67,7 +79,6 @@ vAPI.shutdown = (function() { }; var exec = function() { - //console.debug('Shutting down...'); var job; while ( (job = jobs.pop()) ) { job(); @@ -89,28 +100,34 @@ vAPI.messaging = { pendingCount: 0, auxProcessId: 1, + onDisconnect: function() { + this.port = null; + this.close(); + vAPI.shutdown.exec(); + }, + setup: function() { try { - this.port = chrome.runtime.connect({name: vAPI.sessionId}); + this.port = chrome.runtime.connect({name: vAPI.sessionId}) || null; } catch (ex) { } if ( this.port === null ) { - //console.error("uBlock> Can't patch things up. It's over."); vAPI.shutdown.exec(); return false; } this.port.onMessage.addListener(messagingConnector); + this.port.onDisconnect.addListener(this.onDisconnect.bind(this)); return true; }, close: function() { var port = this.port; - if ( port === null ) { - return; + if ( port !== null ) { + port.disconnect(); + port.onMessage.removeListener(messagingConnector); + port.onDisconnect.removeListener(this.onDisconnect); + this.port = null; } - this.port = null; - port.disconnect(); - port.onMessage.removeListener(messagingConnector); this.channels = {}; // service pending callbacks var pending = this.pending, listener; diff --git a/platform/firefox/vapi-background.js b/platform/firefox/vapi-background.js index e15072456..5e02e0311 100644 --- a/platform/firefox/vapi-background.js +++ b/platform/firefox/vapi-background.js @@ -1440,6 +1440,7 @@ vAPI.setIcon = function(tabId, iconStatus, badge) { if ( tabId === curTabId ) { tb.updateState(win, tabId); + vAPI.contextMenu.onMustUpdate(tabId); } }; @@ -2064,17 +2065,23 @@ var httpObserver = { }, handleResponseHeaders: function(channel, URI, channelData) { - var type = this.typeMap[channelData[3]] || 'other'; - if ( this.onHeadersReceivedTypes && this.onHeadersReceivedTypes.has(type) === false ) { + var requestType = this.typeMap[channelData[3]] || 'other'; + if ( this.onHeadersReceivedTypes && this.onHeadersReceivedTypes.has(requestType) === false ) { return; } // 'Content-Security-Policy' MUST come last in the array. Need to // revised this eventually. var responseHeaders = []; - var value = this.getResponseHeader(channel, 'Content-Security-Policy'); - if ( value !== undefined ) { - responseHeaders.push({ name: 'Content-Security-Policy', value: value }); + var value = channel.contentLength; + if ( value !== -1 ) { + responseHeaders.push({ name: 'Content-Length', value: value }); + } + if ( requestType.endsWith('_frame') ) { + value = this.getResponseHeader(channel, 'Content-Security-Policy'); + if ( value !== undefined ) { + responseHeaders.push({ name: 'Content-Security-Policy', value: value }); + } } // https://github.com/gorhill/uBlock/issues/966 @@ -2088,7 +2095,7 @@ var httpObserver = { parentFrameId: channelData[1], responseHeaders: responseHeaders, tabId: channelData[2], - type: this.typeMap[channelData[3]] || 'other', + type: requestType, url: URI.asciiSpec }); @@ -2179,7 +2186,7 @@ var httpObserver = { // Carry data for behind-the-scene redirects if ( channel instanceof Ci.nsIWritablePropertyBag ) { - channel.setProperty( this.REQDATAKEY, [0, -1, null, vAPI.noTabId, rawtype]); + channel.setProperty(this.REQDATAKEY, [0, -1, vAPI.noTabId, rawtype]); } return; @@ -3171,173 +3178,20 @@ if ( vAPI.toolbarButton.init !== null ) { /******************************************************************************/ /******************************************************************************/ -vAPI.contextMenu = { - contextMap: { +vAPI.contextMenu = (function() { + var clientCallback = null; + var clientEntries = []; + + var contextMap = { frame: 'inFrame', link: 'onLink', image: 'onImage', audio: 'onAudio', video: 'onVideo', editable: 'onEditableArea' - } -}; - -/******************************************************************************/ - -vAPI.contextMenu.displayMenuItem = function({target}) { - var doc = target.ownerDocument; - var gContextMenu = doc.defaultView.gContextMenu; - if ( !gContextMenu.browser ) { - return; - } - - var menuitem = doc.getElementById(vAPI.contextMenu.menuItemId); - var currentURI = gContextMenu.browser.currentURI; - - // https://github.com/chrisaljoudi/uBlock/issues/105 - // TODO: Should the element picker works on any kind of pages? - if ( !currentURI.schemeIs('http') && !currentURI.schemeIs('https') ) { - menuitem.setAttribute('hidden', true); - return; - } - - var ctx = vAPI.contextMenu.contexts; - - if ( !ctx ) { - menuitem.setAttribute('hidden', false); - return; - } - - var ctxMap = vAPI.contextMenu.contextMap; - - for ( var context of ctx ) { - if ( - context === 'page' && - !gContextMenu.onLink && - !gContextMenu.onImage && - !gContextMenu.onEditableArea && - !gContextMenu.inFrame && - !gContextMenu.onVideo && - !gContextMenu.onAudio - ) { - menuitem.setAttribute('hidden', false); - return; - } - if ( - ctxMap.hasOwnProperty(context) && - gContextMenu[ctxMap[context]] - ) { - menuitem.setAttribute('hidden', false); - return; - } - } - - menuitem.setAttribute('hidden', true); -}; - -/******************************************************************************/ - -vAPI.contextMenu.register = (function() { - var register = function(window) { - if ( canRegister(window) !== true ) { - return; - } - - if ( !this.menuItemId ) { - return; - } - - if ( vAPI.fennec ) { - // TODO https://developer.mozilla.org/en-US/Add-ons/Firefox_for_Android/API/NativeWindow/contextmenus/add - /*var nativeWindow = doc.defaultView.NativeWindow; - contextId = nativeWindow.contextmenus.add( - this.menuLabel, - nativeWindow.contextmenus.linkOpenableContext, - this.onCommand - );*/ - return; - } - - // Already installed? - var doc = window.document; - if ( doc.getElementById(this.menuItemId) !== null ) { - return; - } - - var contextMenu = doc.getElementById('contentAreaContextMenu'); - - // This can happen (Thunderbird). - if ( contextMenu === null ) { - return; - } - - var menuitem = doc.createElement('menuitem'); - menuitem.setAttribute('id', this.menuItemId); - menuitem.setAttribute('label', this.menuLabel); - menuitem.setAttribute('image', vAPI.getURL('img/browsericons/icon16.svg')); - menuitem.setAttribute('class', 'menuitem-iconic'); - menuitem.addEventListener('command', this.onCommand); - contextMenu.addEventListener('popupshowing', this.displayMenuItem); - contextMenu.insertBefore(menuitem, doc.getElementById('inspect-separator')); }; - // https://github.com/gorhill/uBlock/issues/906 - // Be sure document.readyState is 'complete': it could happen at launch - // time that we are called by vAPI.contextMenu.create() directly before - // the environment is properly initialized. - var canRegister = function(win) { - return win && win.document.readyState === 'complete'; - }; - - return function(win) { - deferUntil( - canRegister.bind(null, win), - register.bind(this, win) - ); - }; -})(); - -/******************************************************************************/ - -vAPI.contextMenu.unregister = function(win) { - if ( !this.menuItemId ) { - return; - } - - if ( vAPI.fennec ) { - // TODO - return; - } - - var doc = win.document; - var menuitem = doc.getElementById(this.menuItemId); - - // Not guarantee the menu item was actually registered. - if ( menuitem === null ) { - return; - } - - var contextMenu = menuitem.parentNode; - menuitem.removeEventListener('command', this.onCommand); - contextMenu.removeEventListener('popupshowing', this.displayMenuItem); - contextMenu.removeChild(menuitem); -}; - -/******************************************************************************/ - -vAPI.contextMenu.create = function(details, callback) { - this.menuItemId = details.id; - this.menuLabel = details.title; - this.contexts = details.contexts; - - if ( Array.isArray(this.contexts) && this.contexts.length ) { - this.contexts = this.contexts.indexOf('all') === -1 ? this.contexts : null; - } else { - // default in Chrome - this.contexts = ['page']; - } - - this.onCommand = function() { + var onCommand = function() { var gContextMenu = getOwnerWindow(this).gContextMenu; var details = { menuItemId: this.id @@ -3361,29 +3215,185 @@ vAPI.contextMenu.create = function(details, callback) { details.linkUrl = gContextMenu.linkURL; } - callback(details, { + clientCallback(details, { id: tabWatcher.tabIdFromTarget(gContextMenu.browser), url: gContextMenu.browser.currentURI.asciiSpec }); }; - for ( var win of winWatcher.getWindows() ) { - this.register(win); - } -}; + var menuItemMatchesContext = function(contextMenu, clientEntry) { + if ( !clientEntry.contexts ) { + return false; + } + for ( var context of clientEntry.contexts ) { + if ( context === 'all' ) { + return true; + } + if ( + contextMap.hasOwnProperty(context) && + contextMenu[contextMap[context]] + ) { + return true; + } + } + return false; + }; -/******************************************************************************/ + var onMenuShowing = function({target}) { + var doc = target.ownerDocument; + var gContextMenu = doc.defaultView.gContextMenu; + if ( !gContextMenu.browser ) { + return; + } -vAPI.contextMenu.remove = function() { - for ( var win of winWatcher.getWindows() ) { - this.unregister(win); - } + // https://github.com/chrisaljoudi/uBlock/issues/105 + // TODO: Should the element picker works on any kind of pages? + var currentURI = gContextMenu.browser.currentURI, + isHTTP = currentURI.schemeIs('http') || currentURI.schemeIs('https'), + layoutChanged = false, + contextMenu = doc.getElementById('contentAreaContextMenu'), + newEntries = clientEntries, + oldMenuitems = contextMenu.querySelectorAll('[data-uBlock0="menuitem"]'), + newMenuitems = [], + n = Math.max(clientEntries.length, oldMenuitems.length), + menuitem, newEntry; + for ( var i = 0; i < n; i++ ) { + menuitem = oldMenuitems[i]; + newEntry = newEntries[i]; + if ( menuitem && !newEntry ) { + menuitem.parentNode.removeChild(menuitem); + menuitem.removeEventListener('command', onCommand); + menuitem = null; + layoutChanged = true; + } else if ( !menuitem && newEntry ) { + menuitem = doc.createElement('menuitem'); + menuitem.setAttribute('data-uBlock0', 'menuitem'); + menuitem.addEventListener('command', onCommand); + } + if ( !menuitem ) { + continue; + } + if ( menuitem.id !== newEntry.id ) { + menuitem.setAttribute('id', newEntry.id); + menuitem.setAttribute('label', newEntry.title); + layoutChanged = true; + } + menuitem.setAttribute('hidden', !isHTTP || !menuItemMatchesContext(gContextMenu, newEntry)); + newMenuitems.push(menuitem); + } + // No changes? + if ( layoutChanged === false ) { + return; + } + // No entry: remove submenu if present. + var menu = contextMenu.querySelector('[data-uBlock0="menu"]'); + if ( newMenuitems.length === 0 ) { + if ( menu !== null ) { + menu.parentNode.removeChild(menuitem); + } + return; + } + // Only one entry: no need for a submenu. + if ( newMenuitems.length === 1 ) { + if ( menu !== null ) { + menu.parentNode.removeChild(menu); + } + menuitem = newMenuitems[0]; + menuitem.setAttribute('class', 'menuitem-iconic'); + menuitem.setAttribute('image', vAPI.getURL('img/browsericons/icon16.svg')); + contextMenu.insertBefore(menuitem, doc.getElementById('inspect-separator')); + return; + } + // More than one entry: we need a submenu. + if ( menu === null ) { + menu = doc.createElement('menu'); + menu.setAttribute('label', vAPI.app.name); + menu.setAttribute('data-uBlock0', 'menu'); + menu.setAttribute('class', 'menu-iconic'); + menu.setAttribute('image', vAPI.getURL('img/browsericons/icon16.svg')); + contextMenu.insertBefore(menu, doc.getElementById('inspect-separator')); + } + var menupopup = contextMenu.querySelector('[data-uBlock0="menupopup"]'); + if ( menupopup === null ) { + menupopup = doc.createElement('menupopup'); + menupopup.setAttribute('data-uBlock0', 'menupopup'); + menu.appendChild(menupopup); + } + for ( i = 0; i < newMenuitems.length; i++ ) { + menuitem = newMenuitems[i]; + menuitem.setAttribute('class', 'menuitem-non-iconic'); + menuitem.removeAttribute('image'); + menupopup.appendChild(menuitem); + } + }; - this.menuItemId = null; - this.menuLabel = null; - this.contexts = null; - this.onCommand = null; -}; + // https://github.com/gorhill/uBlock/issues/906 + // Be sure document.readyState is 'complete': it could happen at launch + // time that we are called by vAPI.contextMenu.create() directly before + // the environment is properly initialized. + var canRegister = function(win) { + return win && win.document.readyState === 'complete'; + }; + + var register = function(window) { + if ( canRegister(window) !== true ) { + return; + } + + var contextMenu = window.document.getElementById('contentAreaContextMenu'); + if ( contextMenu === null ) { + return; + } + contextMenu.addEventListener('popupshowing', onMenuShowing); + }; + + var registerAsync = function(win) { + // TODO https://developer.mozilla.org/en-US/Add-ons/Firefox_for_Android/API/NativeWindow/contextmenus/add + // var nativeWindow = doc.defaultView.NativeWindow; + // contextId = nativeWindow.contextmenus.add( + // this.menuLabel, + // nativeWindow.contextmenus.linkOpenableContext, + // this.onCommand + // ); + if ( vAPI.fennec ) { + return; + } + deferUntil( + canRegister.bind(null, win), + register.bind(null, win) + ); + }; + + var unregister = function(win) { + // TODO + if ( vAPI.fennec ) { + return; + } + var contextMenu = win.document.getElementById('contentAreaContextMenu'); + if ( contextMenu !== null ) { + contextMenu.removeEventListener('popupshowing', onMenuShowing); + } + var menuitems = win.document.querySelectorAll('[data-uBlock0]'), + menuitem; + for ( var i = 0; i < menuitems.length; i++ ) { + menuitem = menuitems[i]; + menuitem.parentNode.removeChild(menuitem); + menuitem.removeEventListener('command', onCommand); + } + }; + + var setEntries = function(entries, callback) { + clientEntries = entries || []; + clientCallback = callback || null; + }; + + return { + onMustUpdate: function() {}, + register: registerAsync, + unregister: unregister, + setEntries: setEntries + }; +})(); /******************************************************************************/ /******************************************************************************/ diff --git a/platform/firefox/vapi-client.js b/platform/firefox/vapi-client.js index 088f34808..ce9e3d228 100644 --- a/platform/firefox/vapi-client.js +++ b/platform/firefox/vapi-client.js @@ -19,7 +19,9 @@ Home: https://github.com/gorhill/uBlock */ -/* global addMessageListener, removeMessageListener, sendAsyncMessage, outerShutdown */ +/* global HTMLDocument, XMLDocument, + addMessageListener, removeMessageListener, sendAsyncMessage, outerShutdown + */ // For non background pages @@ -29,6 +31,18 @@ 'use strict'; +// https://github.com/chrisaljoudi/uBlock/issues/464 +if ( document instanceof HTMLDocument === false ) { + // https://github.com/chrisaljoudi/uBlock/issues/1528 + // A XMLDocument can be a valid HTML document. + if ( + document instanceof XMLDocument === false || + document.createElement('div') instanceof HTMLDivElement === false + ) { + return; + } +} + /******************************************************************************/ // Not all sandboxes are given an rpc function, so assign a dummy one if it is diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json index 3ebec5abf..50d6f1f0b 100644 --- a/src/_locales/en/messages.json +++ b/src/_locales/en/messages.json @@ -77,19 +77,19 @@ }, "popupTipNoPopups":{ "message":"Toggle the blocking of all popups for this site", - "description":"English: Toggle the blocking of all popups for this site" + "description":"Tooltip for the no-popups per-site switch" }, - "popupTipNoStrictBlocking":{ - "message":"Toggle strict blocking for this site", - "description":"English: Toggle strict blocking for this site" + "popupTipNoLargeMedia":{ + "message":"Toggle the blocking of large media elements for this site", + "description":"Tooltip for the no-large-media per-site switch" }, "popupTipNoCosmeticFiltering":{ "message":"Toggle cosmetic filtering for this site", - "description":"English: Toggle cosmetic filtering for this site" + "description":"Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoRemoteFonts":{ "message":"Toggle the blocking of remote fonts for this site", - "description":"English: Toggle the blocking of remote fonts for this site" + "description":"Tooltip for the no-remote-fonts per-site switch" }, "popupTipGlobalRules":{ "message":"Global rules: this column is for rules which apply to all sites.", @@ -215,9 +215,25 @@ "message":"Prevent WebRTC from leaking local IP addresses", "description":"English: " }, - "settingsExperimentalPrompt":{ - "message":"Enable experimental features (About<\/a>)", - "description":"English: Enable experimental features" + "settingPerSiteSwitchGroup":{ + "message":"Default behavior", + "description": "" + }, + "settingPerSiteSwitchGroupSynopsis":{ + "message":"These default behaviors can be overriden on a per-site basis", + "description": "" + }, + "settingsNoCosmeticFilteringPrompt":{ + "message":"Disable cosmetic filtering", + "description": "" + }, + "settingsNoLargeMediaPrompt":{ + "message":"Block media elements larger than {{input:number}} kB", + "description": "" + }, + "settingsNoRemoteFontsPrompt":{ + "message":"Block remote fonts", + "description": "" }, "settingsStorageUsed":{ "message":"Storage used: {{value}} bytes", @@ -233,23 +249,23 @@ }, "3pListsOfBlockedHostsPrompt":{ "message":"{{netFilterCount}} network filters + {{cosmeticFilterCount}} cosmetic filters from:", - "description":"English: {{netFilterCount}} network filters + {{cosmeticFilterCount}} cosmetic filters from:" + "description":"Appears at the top of the _3rd-party filters_ pane" }, "3pListsOfBlockedHostsPerListStats":{ "message":"{{used}} used out of {{total}}", - "description":"English: {{used}} used out of {{total}}" + "description":"Appears aside each filter list in the _3rd-party filters_ pane" }, "3pAutoUpdatePrompt1":{ "message":"Auto-update filter lists.", - "description":"English: Auto-update filter lists." + "description":"A checkbox in the _3rd-party filters_ pane" }, "3pUpdateNow":{ "message":"Update now", - "description":"English: Update now" + "description":"A button in the in the _3rd-party filters_ pane" }, "3pPurgeAll":{ "message":"Purge all caches", - "description":"English: Purge all caches" + "description":"A button in the in the _3rd-party filters_ pane" }, "3pParseAllABPHideFiltersPrompt1":{ "message":"Parse and enforce cosmetic filters.", @@ -651,6 +667,10 @@ "message": "bytes", "description": "" }, + "contextMenuTemporarilyAllowLargeMediaElements": { + "message": "Temporarily allow large media elements", + "description": "A context menu entry, present when large media elements have been blocked on the current site" + }, "dummy":{ "message":"This entry must be the last one", "description":"so we dont need to deal with comma for last entry" diff --git a/src/css/dashboard-common.css b/src/css/dashboard-common.css index 4922e068c..b5025d84d 100644 --- a/src/css/dashboard-common.css +++ b/src/css/dashboard-common.css @@ -34,10 +34,17 @@ div > p:first-child { div > p:last-child { margin-bottom: 0; } +input[type="number"] { + width: 5em; + } .para { width: 40em; } +.synopsis { + font-size: small; + opacity: 0.8; + } .whatisthis { margin: 0 0 0 8px; border: 0; diff --git a/src/css/popup.css b/src/css/popup.css index b8add2bfb..d4b0b0f7a 100644 --- a/src/css/popup.css +++ b/src/css/popup.css @@ -176,7 +176,7 @@ body.off #switch .fa { #extraTools > span { cursor: pointer; font-size: 18px; - margin: 0 0.4em; + margin: 0 0.5em; position: relative; } #extraTools > span > span.badge { diff --git a/src/js/background.js b/src/js/background.js index 5df92df49..8f922e753 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -61,6 +61,7 @@ return { externalLists: defaultExternalLists, firewallPaneMinimized: true, hyperlinkAuditingDisabled: true, + largeMediaSize: 50, parseAllABPHideFilters: true, prefetchingDisabled: true, requestLogMaxEntries: 1000, diff --git a/src/js/contentscript-end.js b/src/js/contentscript-end.js index 809bf9baf..46f75b39a 100644 --- a/src/js/contentscript-end.js +++ b/src/js/contentscript-end.js @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -/* global vAPI, HTMLDocument, XMLDocument */ - /******************************************************************************/ // Injected into content pages @@ -31,18 +29,6 @@ /******************************************************************************/ -// https://github.com/chrisaljoudi/uBlock/issues/464 -if ( document instanceof HTMLDocument === false ) { - // https://github.com/chrisaljoudi/uBlock/issues/1528 - // A XMLDocument can be a valid HTML document. - if ( - document instanceof XMLDocument === false || - document.createElement('div') instanceof HTMLDivElement === false - ) { - return; - } -} - // I've seen this happens on Firefox if ( window.location === null ) { return; diff --git a/src/js/contentscript-start.js b/src/js/contentscript-start.js index 408771ed1..b6bf3bfbb 100644 --- a/src/js/contentscript-start.js +++ b/src/js/contentscript-start.js @@ -20,7 +20,6 @@ */ /* jshint multistr: true */ -/* global vAPI, HTMLDocument, XMLDocument */ /******************************************************************************/ @@ -34,18 +33,6 @@ /******************************************************************************/ -// https://github.com/chrisaljoudi/uBlock/issues/464 -if ( document instanceof HTMLDocument === false ) { - // https://github.com/chrisaljoudi/uBlock/issues/1528 - // A XMLDocument can be a valid HTML document. - if ( - document instanceof XMLDocument === false || - document.createElement('div') instanceof HTMLDivElement === false - ) { - return; - } -} - // This can happen if ( typeof vAPI !== 'object' ) { //console.debug('contentscript-start.js > vAPI not found'); diff --git a/src/js/contextmenu.js b/src/js/contextmenu.js index a139d2eb9..d02c03bb0 100644 --- a/src/js/contextmenu.js +++ b/src/js/contextmenu.js @@ -1,7 +1,7 @@ /******************************************************************************* - µBlock - a browser extension to block requests. - Copyright (C) 2014 Raymond Hill + uBlock Origin - a browser extension to block requests. + Copyright (C) 2014-2015 Raymond Hill This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,26 +19,19 @@ Home: https://github.com/gorhill/uBlock */ -/* global vAPI, µBlock */ +/******************************************************************************/ + +µBlock.contextMenu = (function() { + 'use strict'; /******************************************************************************/ -// New namespace - -µBlock.contextMenu = (function() { - -/******************************************************************************/ - var µb = µBlock; -var enabled = false; /******************************************************************************/ -var onContextMenuClicked = function(details, tab) { - if ( details.menuItemId !== 'blockElement' ) { - return; - } +var onBlockElement = function(details, tab) { if ( tab === undefined ) { return; } @@ -69,29 +62,90 @@ var onContextMenuClicked = function(details, tab) { /******************************************************************************/ -var toggleMenu = function(on) { - // This needs to be local scope: we can't reuse it for more than one - // menu creation call. - var menuCreateDetails = { - id: 'blockElement', - title: vAPI.i18n('pickerContextMenuEntry'), - contexts: ['page', 'editable', 'frame', 'link', 'image', 'video'], - documentUrlPatterns: ['https://*/*', 'http://*/*'] - }; +var onTemporarilyAllowLargeMediaElements = function(details, tab) { + if ( tab === undefined ) { + return; + } + var pageStore = µb.pageStoreFromTabId(tab.id); + if ( pageStore === null ) { + return; + } + pageStore.temporarilyAllowLargeMediaElements(); +}; - if ( on === true && enabled === false ) { - vAPI.contextMenu.create(menuCreateDetails, onContextMenuClicked); - enabled = true; - } else if ( on !== true && enabled === true ) { - vAPI.contextMenu.remove(); - enabled = false; +/******************************************************************************/ + +var onEntryClicked = function(details, tab) { + if ( details.menuItemId === 'uBlock0-blockElement' ) { + return onBlockElement(details, tab); + } + if ( details.menuItemId === 'uBlock0-temporarilyAllowLargeMediaElements' ) { + return onTemporarilyAllowLargeMediaElements(details, tab); } }; /******************************************************************************/ +var menuEntries = [ + { + id: 'uBlock0-blockElement', + title: vAPI.i18n('pickerContextMenuEntry'), + contexts: ['all'], + documentUrlPatterns: ['https://*/*', 'http://*/*'] + }, + { + id: 'uBlock0-temporarilyAllowLargeMediaElements', + title: vAPI.i18n('contextMenuTemporarilyAllowLargeMediaElements'), + contexts: ['all'], + documentUrlPatterns: ['https://*/*', 'http://*/*'] + } +]; + +/******************************************************************************/ + +var update = function(tabId) { + var newBits = 0; + if ( µb.userSettings.contextMenuEnabled && tabId !== null ) { + var pageStore = µb.pageStoreFromTabId(tabId); + if ( pageStore ) { + newBits |= 0x01; + if ( pageStore.largeMediaCount !== 0 ) { + newBits |= 0x02; + } + } + } + if ( newBits === currentBits ) { + return; + } + currentBits = newBits; + var usedEntries = []; + if ( newBits & 0x01 ) { + usedEntries.push(menuEntries[0]); + } + if ( newBits & 0x02 ) { + usedEntries.push(menuEntries[1]); + } + vAPI.contextMenu.setEntries(usedEntries, onEntryClicked); +}; + +var currentBits = 0; + +vAPI.contextMenu.onMustUpdate = update; + +/******************************************************************************/ + return { - toggle: toggleMenu + update: function(tabId) { + if ( µb.userSettings.contextMenuEnabled && tabId === undefined ) { + vAPI.tabs.get(null, function(tab) { + if ( tab ) { + update(tab.id); + } + }); + return; + } + update(tabId); + } }; /******************************************************************************/ diff --git a/src/js/hnswitches.js b/src/js/hnswitches.js index 0655d41d2..6b2fd0efd 100644 --- a/src/js/hnswitches.js +++ b/src/js/hnswitches.js @@ -40,7 +40,8 @@ var switchBitOffsets = { 'no-strict-blocking': 0, 'no-popups': 2, 'no-cosmetic-filtering': 4, - 'no-remote-fonts': 6 + 'no-remote-fonts': 6, + 'no-large-media': 8 }; var fromLegacySwitchNames = { diff --git a/src/js/messaging.js b/src/js/messaging.js index c76e6360a..fd13e59c0 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -128,10 +128,6 @@ var onMessage = function(request, sender, callback) { response = getDomainNames(request.targets); break; - case 'getUserSettings': - response = µb.userSettings; - break; - case 'launchElementPicker': // Launched from some auxiliary pages, clear context menu coords. µb.mouseX = µb.mouseY = -1; @@ -277,6 +273,7 @@ var getFirewallRules = function(srcHostname, desHostnames) { var popupDataFromTabId = function(tabId, tabTitle) { var tabContext = µb.tabContextManager.mustLookup(tabId); + var rootHostname = tabContext.rootHostname; var r = { advancedUserEnabled: µb.userSettings.advancedUserEnabled, appName: vAPI.app.name, @@ -290,7 +287,7 @@ var popupDataFromTabId = function(tabId, tabTitle) { netFilteringSwitch: false, rawURL: tabContext.rawURL, pageURL: tabContext.normalURL, - pageHostname: tabContext.rootHostname, + pageHostname: rootHostname, pageDomain: tabContext.rootDomain, pageAllowedRequestCount: 0, pageBlockedRequestCount: 0, @@ -307,21 +304,22 @@ var popupDataFromTabId = function(tabId, tabTitle) { r.netFilteringSwitch = pageStore.getNetFilteringSwitch(); r.hostnameDict = getHostnameDict(pageStore.hostnameToCountMap); r.contentLastModified = pageStore.contentLastModified; - r.firewallRules = getFirewallRules(tabContext.rootHostname, r.hostnameDict); - r.canElementPicker = tabContext.rootHostname.indexOf('.') !== -1; - r.noPopups = µb.hnSwitches.evaluateZ('no-popups', tabContext.rootHostname); - r.noStrictBlocking = µb.hnSwitches.evaluateZ('no-strict-blocking', tabContext.rootHostname); - r.noCosmeticFiltering = µb.hnSwitches.evaluateZ('no-cosmetic-filtering', tabContext.rootHostname); - r.noRemoteFonts = µb.hnSwitches.evaluateZ('no-remote-fonts', tabContext.rootHostname); - r.remoteFontCount = pageStore.remoteFontCount; + r.firewallRules = getFirewallRules(rootHostname, r.hostnameDict); + r.canElementPicker = rootHostname.indexOf('.') !== -1; + r.noPopups = µb.hnSwitches.evaluateZ('no-popups', rootHostname); r.popupBlockedCount = pageStore.popupBlockedCount; + r.noCosmeticFiltering = µb.hnSwitches.evaluateZ('no-cosmetic-filtering', rootHostname); + r.noLargeMedia = µb.hnSwitches.evaluateZ('no-large-media', rootHostname); + r.largeMediaCount = pageStore.largeMediaCount; + r.noRemoteFonts = µb.hnSwitches.evaluateZ('no-remote-fonts', rootHostname); + r.remoteFontCount = pageStore.remoteFontCount; } else { r.hostnameDict = {}; r.firewallRules = getFirewallRules(); } r.matrixIsDirty = !µb.sessionFirewall.hasSameRules( µb.permanentFirewall, - tabContext.rootHostname, + rootHostname, r.hostnameDict ); return r; @@ -1342,48 +1340,6 @@ vAPI.messaging.listen('logger-ui.js', onMessage); /******************************************************************************/ /******************************************************************************/ -// subscriber.js - -(function() { - -'use strict'; - -/******************************************************************************/ - -var onMessage = function(request, sender, callback) { - // Async - switch ( request.what ) { - default: - break; - } - - // Sync - var response; - - switch ( request.what ) { - case 'subscriberData': - response = { - confirmStr: vAPI.i18n('subscriberConfirm'), - externalLists: µBlock.userSettings.externalLists - }; - break; - - default: - return vAPI.messaging.UNHANDLED; - } - - callback(response); -}; - -vAPI.messaging.listen('subscriber.js', onMessage); - -/******************************************************************************/ - -})(); - -/******************************************************************************/ -/******************************************************************************/ - // document-blocked.js (function() { @@ -1461,6 +1417,7 @@ var logCosmeticFilters = function(tabId, details) { var onMessage = function(request, sender, callback) { var tabId = sender && sender.tab ? sender.tab.id : 0; + var pageStore = µb.pageStoreFromTabId(tabId); // Async switch ( request.what ) { @@ -1473,8 +1430,7 @@ var onMessage = function(request, sender, callback) { switch ( request.what ) { case 'liveCosmeticFilteringData': - var pageStore = µb.pageStoreFromTabId(tabId); - if ( pageStore ) { + if ( pageStore !== null ) { pageStore.hiddenElementCount = request.filteredElementCount; } break; @@ -1483,6 +1439,19 @@ var onMessage = function(request, sender, callback) { logCosmeticFilters(tabId, request); break; + case 'temporarilyAllowLargeMediaElement': + if ( pageStore !== null ) { + pageStore.allowLargeMediaElementsUntil = Date.now() + 2000; + } + break; + + case 'subscriberData': + response = { + confirmStr: vAPI.i18n('subscriberConfirm'), + externalLists: µBlock.userSettings.externalLists + }; + break; + default: return vAPI.messaging.UNHANDLED; } diff --git a/src/js/pagestore.js b/src/js/pagestore.js index 8ed1103d3..f43b35cd3 100644 --- a/src/js/pagestore.js +++ b/src/js/pagestore.js @@ -294,6 +294,16 @@ PageStore.factory = function(tabId) { PageStore.prototype.init = function(tabId) { var tabContext = µb.tabContextManager.mustLookup(tabId); this.tabId = tabId; + + // If we are navigating from-to same site, remember whether large + // media elements were temporarily allowed. + if ( + typeof this.allowLargeMediaElementsUntil !== 'number' || + tabContext.rootHostname !== this.tabHostname + ) { + this.allowLargeMediaElementsUntil = 0; + } + this.tabHostname = tabContext.rootHostname; this.title = tabContext.rawURL; this.rawURL = tabContext.rawURL; @@ -305,6 +315,8 @@ PageStore.prototype.init = function(tabId) { this.hiddenElementCount = ''; // Empty string means "unknown" this.remoteFontCount = 0; this.popupBlockedCount = 0; + this.largeMediaCount = 0; + this.largeMediaTimer = null; this.netFilteringCache = NetFilteringResultCache.factory(); // Support `elemhide` filter option. Called at this point so the required @@ -354,6 +366,10 @@ PageStore.prototype.reuse = function(context) { } // A new page is completely reloaded from scratch, reset all. + if ( this.largeMediaTimer !== null ) { + clearTimeout(this.largeMediaTimer); + this.largeMediaTimer = null; + } this.disposeFrameStores(); this.netFilteringCache = this.netFilteringCache.dispose(); this.init(this.tabId); @@ -373,6 +389,11 @@ PageStore.prototype.dispose = function() { this.title = ''; this.rawURL = ''; this.hostnameToCountMap = null; + this.allowLargeMediaElementsUntil = 0; + if ( this.largeMediaTimer !== null ) { + clearTimeout(this.largeMediaTimer); + this.largeMediaTimer = null; + } this.disposeFrameStores(); this.netFilteringCache = this.netFilteringCache.dispose(); if ( pageStoreJunkyard.length < pageStoreJunkyardMax ) { @@ -469,6 +490,32 @@ PageStore.prototype.toggleNetFilteringSwitch = function(url, scope, state) { /******************************************************************************/ +PageStore.prototype.logLargeMedia = (function() { + var injectScript = function() { + this.largeMediaTimer = null; + µb.scriptlets.injectDeep( + this.tabId, + 'load-large-media-interactive' + ); + µb.contextMenu.update(this.tabId); + }; + return function() { + this.largeMediaCount += 1; + if ( this.largeMediaTimer === null ) { + this.largeMediaTimer = vAPI.setTimeout(injectScript.bind(this), 500); + } + }; +})(); + +PageStore.prototype.temporarilyAllowLargeMediaElements = function() { + this.largeMediaCount = 0; + µb.contextMenu.update(this.tabId); + this.allowLargeMediaElementsUntil = Date.now() + 86400000; + µb.scriptlets.injectDeep(this.tabId, 'load-large-media-all'); +}; + +/******************************************************************************/ + PageStore.prototype.filterRequest = function(context) { var requestType = context.requestType; diff --git a/src/js/popup.js b/src/js/popup.js index d2776b2e7..5fa84bbeb 100644 --- a/src/js/popup.js +++ b/src/js/popup.js @@ -160,6 +160,7 @@ var hashFromPopupData = function(reset) { } hasher.sort(); hasher.push(uDom('body').hasClass('off')); + hasher.push(uDom.nodeFromId('no-large-media').classList.contains('on')); hasher.push(uDom.nodeFromId('no-cosmetic-filtering').classList.contains('on')); hasher.push(uDom.nodeFromId('no-remote-fonts').classList.contains('on')); @@ -459,7 +460,7 @@ var renderPopup = function() { // Extra tools uDom.nodeFromId('no-popups').classList.toggle('on', popupData.noPopups === true); - uDom.nodeFromId('no-strict-blocking').classList.toggle('on', popupData.noStrictBlocking === true); + uDom.nodeFromId('no-large-media').classList.toggle('on', popupData.noLargeMedia === true); uDom.nodeFromId('no-cosmetic-filtering').classList.toggle('on', popupData.noCosmeticFiltering === true); uDom.nodeFromId('no-remote-fonts').classList.toggle('on', popupData.noRemoteFonts === true); @@ -468,6 +469,11 @@ var renderPopup = function() { uDom.nodeFromSelector('#no-popups > span.badge') .textContent = total ? total.toLocaleString() : ''; + // Report large media count on badge + total = popupData.largeMediaCount; + uDom.nodeFromSelector('#no-large-media > span.badge') + .textContent = total ? total.toLocaleString() : ''; + // Report remote font count on badge total = popupData.remoteFontCount; uDom.nodeFromSelector('#no-remote-fonts > span.badge') @@ -733,18 +739,18 @@ var revertFirewallRules = function() { /******************************************************************************/ -var toggleHostnameSwitch = function() { - var elem = uDom(this); - var switchName = elem.attr('id'); +var toggleHostnameSwitch = function(ev) { + var target = ev.currentTarget; + var switchName = target.getAttribute('id'); if ( !switchName ) { return; } - elem.toggleClass('on'); + target.classList.toggle('on'); messager.send({ what: 'toggleHostnameSwitch', name: switchName, hostname: popupData.pageHostname, - state: elem.hasClass('on'), + state: target.classList.contains('on'), tabId: popupData.tabId }); hashFromPopupData(); diff --git a/src/js/scriptlets/cosmetic-logger.js b/src/js/scriptlets/cosmetic-logger.js index a36a48e2f..7040c164e 100644 --- a/src/js/scriptlets/cosmetic-logger.js +++ b/src/js/scriptlets/cosmetic-logger.js @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -/* global vAPI, HTMLDocument, XMLDocument */ - /******************************************************************************/ (function() { @@ -29,19 +27,6 @@ /******************************************************************************/ -// https://github.com/gorhill/uBlock/issues/464 -if ( document instanceof HTMLDocument === false ) { - // https://github.com/chrisaljoudi/uBlock/issues/1528 - // A XMLDocument can be a valid HTML document. - if ( - document instanceof XMLDocument === false || - document.createElement('div') instanceof HTMLDivElement === false - ) { - return; - } -} - -// This can happen if ( typeof vAPI !== 'object' ) { return; } diff --git a/src/js/scriptlets/cosmetic-off.js b/src/js/scriptlets/cosmetic-off.js index b94cfe6cc..a99ae9c45 100644 --- a/src/js/scriptlets/cosmetic-off.js +++ b/src/js/scriptlets/cosmetic-off.js @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -/* global vAPI, HTMLDocument, XMLDocument */ - /******************************************************************************/ (function() { @@ -29,21 +27,7 @@ /******************************************************************************/ -// https://github.com/gorhill/uBlock/issues/464 -if ( document instanceof HTMLDocument === false ) { - // https://github.com/chrisaljoudi/uBlock/issues/1528 - // A XMLDocument can be a valid HTML document. - if ( - document instanceof XMLDocument === false || - document.createElement('div') instanceof HTMLDivElement === false - ) { - return; - } -} - -// This can happen if ( typeof vAPI !== 'object' ) { - //console.debug('cosmetic-off.js > no vAPI'); return; } diff --git a/src/js/scriptlets/cosmetic-on.js b/src/js/scriptlets/cosmetic-on.js index affe19172..78d12f34d 100644 --- a/src/js/scriptlets/cosmetic-on.js +++ b/src/js/scriptlets/cosmetic-on.js @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -/* global vAPI, HTMLDocument, XMLDocument */ - /******************************************************************************/ (function() { @@ -29,21 +27,7 @@ /******************************************************************************/ -// https://github.com/gorhill/uBlock/issues/464 -if ( document instanceof HTMLDocument === false ) { - // https://github.com/chrisaljoudi/uBlock/issues/1528 - // A XMLDocument can be a valid HTML document. - if ( - document instanceof XMLDocument === false || - document.createElement('div') instanceof HTMLDivElement === false - ) { - return; - } -} - -// This can happen if ( typeof vAPI !== 'object' ) { - //console.debug('cosmetic-on.js > no vAPI'); return; } diff --git a/src/js/scriptlets/cosmetic-survey.js b/src/js/scriptlets/cosmetic-survey.js index 7e9129a09..3d62ed5d3 100644 --- a/src/js/scriptlets/cosmetic-survey.js +++ b/src/js/scriptlets/cosmetic-survey.js @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -/* global vAPI, HTMLDocument, XMLDocument */ - /******************************************************************************/ (function() { @@ -29,21 +27,7 @@ /******************************************************************************/ -// https://github.com/gorhill/uBlock/issues/464 -if ( document instanceof HTMLDocument === false ) { - // https://github.com/chrisaljoudi/uBlock/issues/1528 - // A XMLDocument can be a valid HTML document. - if ( - document instanceof XMLDocument === false || - document.createElement('div') instanceof HTMLDivElement === false - ) { - return; - } -} - -// This can happen if ( typeof vAPI !== 'object' ) { - //console.debug('cosmetic-survey.js > vAPI not found'); return; } diff --git a/src/js/scriptlets/dom-inspector.js b/src/js/scriptlets/dom-inspector.js index e2352d0e9..d33926282 100644 --- a/src/js/scriptlets/dom-inspector.js +++ b/src/js/scriptlets/dom-inspector.js @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -/* global vAPI, HTMLDocument, XMLDocument */ - /******************************************************************************/ /******************************************************************************/ @@ -30,19 +28,6 @@ /******************************************************************************/ -// https://github.com/gorhill/uBlock/issues/464 -if ( document instanceof HTMLDocument === false ) { - // https://github.com/chrisaljoudi/uBlock/issues/1528 - // A XMLDocument can be a valid HTML document. - if ( - document instanceof XMLDocument === false || - document.createElement('div') instanceof HTMLDivElement === false - ) { - return; - } -} - -// This can happen if ( typeof vAPI !== 'object' ) { return; } diff --git a/src/js/scriptlets/element-picker.js b/src/js/scriptlets/element-picker.js index 8fae47338..f150a9f1c 100644 --- a/src/js/scriptlets/element-picker.js +++ b/src/js/scriptlets/element-picker.js @@ -19,7 +19,7 @@ Home: https://github.com/gorhill/uBlock */ -/* global self, vAPI, CSS, HTMLDocument, XMLDocument */ +/* global CSS */ /******************************************************************************/ /******************************************************************************/ @@ -66,16 +66,16 @@ if ( // If the character is in the range [\1-\1F] (U+0001 to U+001F) or is // U+007F, […] - (codeUnit >= 0x0001 && codeUnit <= 0x001F) || codeUnit == 0x007F || + (codeUnit >= 0x0001 && codeUnit <= 0x001F) || codeUnit === 0x007F || // If the character is the first character and is in the range [0-9] // (U+0030 to U+0039), […] (index === 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) || // If the character is the second character and is in the range [0-9] // (U+0030 to U+0039) and the first character is a `-` (U+002D), […] ( - index == 1 && + index === 1 && codeUnit >= 0x0030 && codeUnit <= 0x0039 && - firstCodeUnit == 0x002D + firstCodeUnit === 0x002D ) ) { // http://dev.w3.org/csswg/cssom/#escape-a-character-as-code-point @@ -89,8 +89,8 @@ // U+005A), or [a-z] (U+0061 to U+007A), […] if ( codeUnit >= 0x0080 || - codeUnit == 0x002D || - codeUnit == 0x005F || + codeUnit === 0x002D || + codeUnit === 0x005F || codeUnit >= 0x0030 && codeUnit <= 0x0039 || codeUnit >= 0x0041 && codeUnit <= 0x005A || codeUnit >= 0x0061 && codeUnit <= 0x007A @@ -120,20 +120,10 @@ /******************************************************************************/ -// https://github.com/gorhill/uBlock/issues/464 -if ( document instanceof HTMLDocument === false ) { - // https://github.com/chrisaljoudi/uBlock/issues/1528 - // A XMLDocument can be a valid HTML document. - if ( - document instanceof XMLDocument === false || - document.createElement('div') instanceof HTMLDivElement === false - ) { - return; - } +if ( typeof vAPI !== 'object' ) { + return; } -/******************************************************************************/ - // don't run in frames if ( window.top !== window ) { return; diff --git a/src/js/scriptlets/load-large-media-all.js b/src/js/scriptlets/load-large-media-all.js new file mode 100644 index 000000000..182ac1c36 --- /dev/null +++ b/src/js/scriptlets/load-large-media-all.js @@ -0,0 +1,69 @@ +/******************************************************************************* + + uBlock Origin - a browser extension to block requests. + Copyright (C) 2015 Raymond Hill + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see {http://www.gnu.org/licenses/}. + + Home: https://github.com/gorhill/uBlock +*/ + +/******************************************************************************/ + +(function() { + +'use strict'; + +/******************************************************************************/ + +// For all media resources which have failed to load, trigger a reload. + +var elems, i, elem, src; + +//
  • +
  • diff --git a/tools/make-firefox-meta.py b/tools/make-firefox-meta.py index 72e0c280e..974e67394 100644 --- a/tools/make-firefox-meta.py +++ b/tools/make-firefox-meta.py @@ -24,7 +24,7 @@ source_locale_dir = pj(build_dir, '_locales') target_locale_dir = pj(build_dir, 'locale') language_codes = [] descriptions = OrderedDict({}) -title_case_strings = ['pickerContextMenuEntry'] +title_case_strings = ['pickerContextMenuEntry', 'contextMenuTemporarilyAllowLargeMediaElements'] for alpha2 in sorted(os.listdir(source_locale_dir)): locale_path = pj(source_locale_dir, alpha2, 'messages.json')