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);
-};
/******************************************************************************/