diff --git a/src/js/element-picker.js b/src/js/element-picker.js index 1a37e0a34..725b59506 100644 --- a/src/js/element-picker.js +++ b/src/js/element-picker.js @@ -19,7 +19,7 @@ Home: https://github.com/gorhill/uBlock */ -/* global messager, CSS */ +/* global CSS */ 'use strict'; /******************************************************************************/ @@ -120,6 +120,8 @@ /******************************************************************************/ +var localMessager = vAPI.messaging.channel('element-picker.js'); + // https://github.com/gorhill/uBlock/issues/314#issuecomment-58878112 // Using an id makes uBlock's CSS rules more specific, thus prevents // surrounding external rules from winning over own rules. @@ -203,11 +205,11 @@ var highlightElements = function(elems, force) { } targetElements = elems; - var ow = svgRoot.getAttribute('width'); + var ow = parseInt(svgRoot.style.width, 10); var ocean = [ 'M0 0', 'h', ow, - 'v', svgRoot.getAttribute('height'), + 'v', parseInt(svgRoot.style.height, 10), 'h-', ow, 'z' ]; @@ -231,7 +233,7 @@ var highlightElements = function(elems, force) { islands.push(poly); } svgOcean.setAttribute('d', ocean.join('')); - svgIslands.setAttribute('d', islands.join('')); + svgIslands.setAttribute('d', islands.join('') || 'M 0 0'); }; /******************************************************************************/ @@ -502,7 +504,7 @@ var onDialogClicked = function(ev) { else if ( ev.target.id === 'create' ) { var filter = userFilterFromCandidate(); if ( filter ) { - messager.send({ what: 'createUserFilter', filters: filter }); + localMessager.send({ what: 'createUserFilter', filters: filter }); removeElements(elementsFromFilter(taCandidate.value)); stopPicker(); } @@ -588,12 +590,12 @@ var showDialog = function(options) { /******************************************************************************/ var elementFromPoint = function(x, y) { - svgRoot.style.pointerEvents = 'none'; + svgRoot.style.display = 'none'; var elem = document.elementFromPoint(x, y); if ( elem === document.body || elem === document.documentElement ) { elem = null; } - svgRoot.style.pointerEvents = 'auto'; + svgRoot.style.display = ''; return elem; }; @@ -641,8 +643,8 @@ var onScrolled = function() { var newHeight = this.scrollY + this.innerHeight; if ( newHeight > svgHeight ) { svgHeight = newHeight; - svgRoot.setAttribute('height', svgHeight); - svgRoot.setAttribute("viewBox", '0 0 ' + svgWidth + ' ' + svgHeight); + svgRoot.style.height = svgHeight + 'px'; + svgRoot.setAttribute('viewBox', '0 0 ' + svgWidth + ' ' + svgHeight); } highlightElements(targetElements, true); }; @@ -654,19 +656,19 @@ var onScrolled = function() { var stopPicker = function() { if ( pickerRoot !== null ) { - document.removeEventListener('keydown', onKeyPressed); - window.removeEventListener('scroll', onScrolled); + window.removeEventListener('keydown', onKeyPressed, true); + window.removeEventListener('scroll', onScrolled, true); taCandidate.removeEventListener('input', onCandidateChanged); divDialog.removeEventListener('click', onDialogClicked); svgRoot.removeEventListener('mousemove', onSvgHovered); svgRoot.removeEventListener('click', onSvgClicked); - pickerRoot.parentNode.removeChild(pickerRoot) + pickerRoot.parentNode.removeChild(pickerRoot); pickerRoot = divDialog = svgRoot = svgOcean = svgIslands = taCandidate = urlNormalizer = null; - messager.close(); + localMessager.close(); } targetElements = []; }; @@ -740,7 +742,6 @@ var startPicker = function() { 'position: absolute;', 'top: 0;', 'left: 0;', - 'pointer-events: auto;', 'cursor: crosshair;', 'z-index: 4999999999;', '}', @@ -846,7 +847,8 @@ var startPicker = function() { pickerRoot.appendChild(pickerStyle); svgRoot = document.createElementNS(svgns, 'svg'); - svgRoot.innerHTML = ''; + svgRoot.appendChild(document.createElementNS(svgns, 'path')); + svgRoot.appendChild(document.createElementNS(svgns, 'path')); svgWidth = document.documentElement.scrollWidth; svgHeight = Math.max( document.documentElement.scrollHeight, @@ -854,11 +856,11 @@ var startPicker = function() { ); svgRoot.setAttribute('x', 0); svgRoot.setAttribute('y', 0); - svgRoot.setAttribute('width', svgWidth); - svgRoot.setAttribute('height', svgHeight); - svgRoot.setAttribute("viewBox", '0 0 ' + svgWidth + ' ' + svgHeight); - svgOcean = svgRoot.querySelector('path:first-child'); - svgIslands = svgRoot.querySelector('path + path'); + svgRoot.style.width = svgWidth + 'px'; + svgRoot.style.height = svgHeight + 'px'; + svgRoot.setAttribute('viewBox', '0 0 ' + svgWidth + ' ' + svgHeight); + svgOcean = svgRoot.firstChild; + svgIslands = svgRoot.lastChild; pickerRoot.appendChild(svgRoot); // TODO: do not rely on element ids, they could collide with whatever @@ -892,8 +894,8 @@ var startPicker = function() { taCandidate = divDialog.querySelector('textarea'); taCandidate.addEventListener('input', onCandidateChanged); urlNormalizer = document.createElement('a'); - window.addEventListener('scroll', onScrolled); - document.addEventListener('keydown', onKeyPressed); + window.addEventListener('scroll', onScrolled, true); + window.addEventListener('keydown', onKeyPressed, true); highlightElements([], true); @@ -970,13 +972,16 @@ var startPicker = function() { } }; - messager.send({ what: 'elementPickerArguments' }, initPicker); + localMessager.send({ what: 'elementPickerArguments' }, initPicker); }; /******************************************************************************/ startPicker(); +// This triggers the hiding of the popover in Safari +window.focus(); + /******************************************************************************/ // https://www.youtube.com/watch?v=sociXdKnyr8 diff --git a/src/js/messaging.js b/src/js/messaging.js index 04cb2b3df..8086b65c0 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -339,9 +339,41 @@ var onMessage = function(details, sender, callback) { response = filterRequest(pageStore, details); } break; + } + callback(response); +}; - // the following is used by element-picker.js +vAPI.messaging.listen('contentscript-end.js', onMessage); + +/******************************************************************************/ + +})(); + +/******************************************************************************/ +/******************************************************************************/ + +// element-picker.js + +(function() { + +/******************************************************************************/ + +var µb = µBlock; + +/******************************************************************************/ + +var onMessage = function(request, sender, callback) { + // Async + switch ( request.what ) { + default: + break; + } + + // Sync + var response; + + switch ( request.what ) { case 'elementPickerArguments': response = { i18n: { @@ -371,7 +403,7 @@ var onMessage = function(details, sender, callback) { callback(response); }; -vAPI.messaging.listen('contentscript-end.js', onMessage); +vAPI.messaging.listen('element-picker.js', onMessage); /******************************************************************************/ diff --git a/src/js/storage.js b/src/js/storage.js index 6b634201a..bdd51b746 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -602,14 +602,14 @@ if ( chrome.runtime.lastError ) { return; } - chrome.tabs.executeScript(tabId, { + vAPI.tabs.injectScript(tabId, { file: 'js/contentscript-end.js', allFrames: true, runAt: 'document_idle' }, scriptDone); }; var scriptStart = function(tabId) { - chrome.tabs.executeScript(tabId, { + vAPI.tabs.injectScript(tabId, { file: 'js/contentscript-start.js', allFrames: true, runAt: 'document_idle' diff --git a/src/js/ublock.js b/src/js/ublock.js index 56fc6adb7..7b32d0776 100644 --- a/src/js/ublock.js +++ b/src/js/ublock.js @@ -252,7 +252,7 @@ µBlock.elementPickerExec = function(tabId, targetElement) { this.elementPickerTarget = targetElement || ''; - this.XAL.injectScript(tabId, { file: 'js/element-picker.js' }); + vAPI.tabs.injectScript(tabId, { file: 'js/element-picker.js' }); }; /******************************************************************************/ diff --git a/src/js/vapi-background.js b/src/js/vapi-background.js index 6580403ad..117fb9ebe 100644 --- a/src/js/vapi-background.js +++ b/src/js/vapi-background.js @@ -136,7 +136,19 @@ if (window.chrome) { wrapper(); } }, - close: chrome.tabs.remove.bind(chrome.tabs) + close: chrome.tabs.remove.bind(chrome.tabs), + injectScript: function(tabId, details, callback) { + if (!callback) { + callback = function(){}; + } + + if (tabId) { + chrome.tabs.executeScript(tabId, details, callback); + } + else { + chrome.tabs.executeScript(details, callback); + } + } }; // Must read: https://code.google.com/p/chromium/issues/detail?id=410868#c8 @@ -176,7 +188,7 @@ if (window.chrome) { var onMessage = function(request) { var callback = function(response) { // stfu - if (chrome.runtime.lastError) { + if (chrome.runtime.lastError || response === undefined) { return; } @@ -492,6 +504,29 @@ if (window.chrome) { if (tab) { tab.close(); } + }, + injectScript: function(tabId, details, callback) { + var tab = tabId ? this.stack[tabId] : safari.application.activeBrowserWindow.activeTab; + + if (details.file) { + var xhr = new XMLHttpRequest; + xhr.overrideMimeType('application/x-javascript;charset=utf-8'); + xhr.open('GET', details.file, false); + xhr.send(); + details.code = xhr.responseText; + } + + tab.page.dispatchMessage('message', { + portName: 'vAPI', + msg: { + cmd: 'runScript', + details: details + } + }); + + if (typeof callback === 'function') { + setTimeout(callback, 13); + } } }; @@ -624,7 +659,7 @@ if (window.chrome) { } var callback = function(response) { - if (request.message.requestId) { + if (request.message.requestId && response !== undefined) { request.target.page.dispatchMessage( 'message', { diff --git a/src/js/vapi-client.js b/src/js/vapi-client.js index d97d35f45..8367fb6e4 100644 --- a/src/js/vapi-client.js +++ b/src/js/vapi-client.js @@ -27,11 +27,11 @@ var messagingConnector = function(response) { if (response.requestId) { listener = vAPI.messaging.listeners[response.requestId]; + } - if (!listener) { - channel = vAPI.messaging.channels[response.portName]; - listener = channel && channel.listener; - } + if (!listener) { + channel = vAPI.messaging.channels[response.portName]; + listener = channel && channel.listener; } if (typeof listener === 'function') { @@ -230,7 +230,6 @@ if (window.chrome) { // relevant? // https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/MessagesandProxies/MessagesandProxies.html#//apple_ref/doc/uid/TP40009977-CH14-SW12 vAPI.messaging = { - port: null, requestId: 0, listeners: {}, channels: {}, @@ -240,10 +239,19 @@ if (window.chrome) { vAPI.messaging.connector(msg.message); }; safari.self.addEventListener('message', this._connector, false); + + this.channels['vAPI'] = { + listener: function(msg) { + if (msg.cmd === 'runScript' && msg.details.code) { + Function(msg.details.code).call(window); + } + } + }; }, close: function() { if (this._connector) { safari.self.removeEventListener('message', this._connector, false); + this.channels = this.listeners = null; } }, channel: function(name, callback) { diff --git a/src/js/xal.js b/src/js/xal.js index 5ce6ecf60..77814859e 100644 --- a/src/js/xal.js +++ b/src/js/xal.js @@ -31,12 +31,6 @@ var exports = {}; var noopFunc = function(){}; -/******************************************************************************/ - - -exports.injectScript = function(id, details) { - chrome.tabs.executeScript(id, details); -}; /******************************************************************************/