Element picker: cleanup and Firefox improvements (closes #1211)

In Firefox, you can now block an element right from the Web Inspector.
This was implemented by @AlexVallat -- thanks to Alex for the awesome
feature enhancement.
This commit is contained in:
Chris 2015-04-11 23:23:22 -06:00
parent f961cad0f0
commit e8572bdc21
6 changed files with 76 additions and 74 deletions

View File

@ -65,7 +65,7 @@ vAPI.app.restart = function() {
var cleanupTasks = []; var cleanupTasks = [];
// This must be updated manually, every time a new task is added/removed // This must be updated manually, every time a new task is added/removed
var expectedNumberOfCleanups = 6; // 7 instances of cleanupTasks.push, but one is unique to fennec, and one to desktop. var expectedNumberOfCleanups = vAPI.fennec ? 7 : 8; // 8 instances of cleanupTasks.push, but one is unique to fennec, and two to desktop.
window.addEventListener('unload', function() { window.addEventListener('unload', function() {
for ( var cleanup of cleanupTasks ) { for ( var cleanup of cleanupTasks ) {
@ -392,8 +392,9 @@ vAPI.tabs.registerListeners = function() {
cleanupTasks.push(function() { cleanupTasks.push(function() {
Services.ww.unregisterNotification(windowWatcher); Services.ww.unregisterNotification(windowWatcher);
vAPI.contextMenu.remove();
for ( var win of vAPI.tabs.getWindows() ) { for ( var win of vAPI.tabs.getWindows() ) {
vAPI.contextMenu.unregister(win.document);
win.removeEventListener('DOMContentLoaded', windowWatcher.onReady); win.removeEventListener('DOMContentLoaded', windowWatcher.onReady);
var tabContainer; var tabContainer;
@ -1760,6 +1761,15 @@ vAPI.contextMenu.displayMenuItem = function({target}) {
/******************************************************************************/ /******************************************************************************/
vAPI.contextMenu.createContextMenuItem = function(doc) {
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');
return menuitem;
}
vAPI.contextMenu.register = function(doc) { vAPI.contextMenu.register = function(doc) {
if ( !this.menuItemId ) { if ( !this.menuItemId ) {
return; return;
@ -1777,11 +1787,7 @@ vAPI.contextMenu.register = function(doc) {
} }
var contextMenu = doc.getElementById('contentAreaContextMenu'); var contextMenu = doc.getElementById('contentAreaContextMenu');
var menuitem = doc.createElement('menuitem'); var menuitem = this.createContextMenuItem(doc);
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); menuitem.addEventListener('command', this.onCommand);
contextMenu.addEventListener('popupshowing', this.displayMenuItem); contextMenu.addEventListener('popupshowing', this.displayMenuItem);
contextMenu.insertBefore(menuitem, doc.getElementById('inspect-separator')); contextMenu.insertBefore(menuitem, doc.getElementById('inspect-separator'));
@ -1808,6 +1814,36 @@ vAPI.contextMenu.unregister = function(doc) {
/******************************************************************************/ /******************************************************************************/
vAPI.contextMenu.registerForWebInspector = function(eventName, toolbox, panel) {
var menuPopup = panel.panelDoc.getElementById("inspector-node-popup");
var deleteMenuItem = panel.panelDoc.getElementById("node-menu-delete");
var tiltButton = toolbox.toolboxButtons.filter(tool => tool.id === "command-button-tilt")[0];
tiltButton = tiltButton && tiltButton.button;
if (menuPopup && deleteMenuItem) {
var menuitem = vAPI.contextMenu.createContextMenuItem(panel.panelDoc);
menuitem.addEventListener('command', function() {
var selectedNodeFront = panel.selection.nodeFront;
while (selectedNodeFront && selectedNodeFront.baseURI !== panel.walker.rootNode.baseURI) {
// This is an iFrame, so we can't select it directly. Walk up the parent stack until we do
selectedNodeFront = selectedNodeFront.parentNode();
}
if (selectedNodeFront) {
selectedNodeFront.getUniqueSelector().then(selector => µBlock.elementPickerExec(vAPI.tabs.getTabId(panel.browser), selector));
// Turn off 3D view, if it's turned on.
if (tiltButton && tiltButton.checked) {
tiltButton.click();
}
}
});
menuPopup.insertBefore(menuitem, deleteMenuItem);
}
}
/******************************************************************************/
vAPI.contextMenu.create = function(details, callback) { vAPI.contextMenu.create = function(details, callback) {
this.menuItemId = details.id; this.menuItemId = details.id;
this.menuLabel = details.title; this.menuLabel = details.title;
@ -1850,6 +1886,19 @@ vAPI.contextMenu.create = function(details, callback) {
}); });
}; };
// Also add a context menu to the web inspector
if (!vAPI.fennec) {
try {
this.gDevTools = Cu.import('resource:///modules/devtools/gDevTools.jsm', null).gDevTools;
} catch (ex) {
// console.error(ex);
}
if (this.gDevTools) {
this.gDevTools.on("inspector-ready", this.registerForWebInspector);
}
}
for ( var win of vAPI.tabs.getWindows() ) { for ( var win of vAPI.tabs.getWindows() ) {
this.register(win.document); this.register(win.document);
} }
@ -1862,6 +1911,10 @@ vAPI.contextMenu.remove = function() {
this.unregister(win.document); this.unregister(win.document);
} }
if (!vAPI.fennec && this.gDevTools) {
this.gDevTools.off("inspector-ready", this.registerForWebInspector);
}
this.menuItemId = null; this.menuItemId = null;
this.menuLabel = null; this.menuLabel = null;
this.contexts = null; this.contexts = null;

View File

@ -136,11 +136,10 @@ return {
noopFunc: function(){}, noopFunc: function(){},
apiErrorCount: 0, apiErrorCount: 0,
contextMenuTarget: '',
contextMenuClientX: -1, contextMenuClientX: -1,
contextMenuClientY: -1, contextMenuClientY: -1,
epickerTarget: '', epickerTargetElementSelector: null,
epickerEprom: null, epickerEprom: null,
// so that I don't have to care for last comma // so that I don't have to care for last comma

View File

@ -45,26 +45,8 @@ var onContextMenuClicked = function(details, tab) {
if ( /^https?:\/\//.test(tab.url) === false ) { if ( /^https?:\/\//.test(tab.url) === false ) {
return; return;
} }
var tagName = details.tagName || '';
var src = details.frameUrl || details.srcUrl || details.linkUrl || '';
if ( !tagName ) { µb.elementPickerExec(tab.id);
if ( typeof details.frameUrl === 'string' ) {
tagName = 'iframe';
} else if ( typeof details.srcUrl === 'string' ) {
if ( details.mediaType === 'image' ) {
tagName = 'img';
} else if ( details.mediaType === 'video' ) {
tagName = 'video';
} else if ( details.mediaType === 'audio' ) {
tagName = 'audio';
}
} else if ( typeof details.linkUrl === 'string' ) {
tagName = 'a';
}
}
µb.elementPickerExec(tab.id, tagName + '\t' + src);
}; };
/******************************************************************************/ /******************************************************************************/

View File

@ -128,7 +128,8 @@ if ( window.top !== window ) {
var pickerRoot = document.getElementById(vAPI.sessionId); var pickerRoot = document.getElementById(vAPI.sessionId);
if ( pickerRoot ) { if ( pickerRoot ) {
return; // If it's already running, stop it and then allow it to restart
pickerRoot.onload(); // Calls stopPicker
} }
var localMessager = vAPI.messaging.channel('element-picker.js'); var localMessager = vAPI.messaging.channel('element-picker.js');
@ -873,53 +874,21 @@ var startPicker = function(details) {
highlightElements([], true); highlightElements([], true);
var elem; var elem = null;
// If a target element was provided, use it
if (details.targetElementSelector) {
elem = document.querySelector(details.targetElementSelector);
}
// Try using mouse position // Try using mouse position
if ( details.clientX !== -1 ) { if (!elem && details.clientX !== -1) {
elem = elementFromPoint(details.clientX, details.clientY); elem = elementFromPoint(details.clientX, details.clientY);
}
if (elem !== null) { if (elem !== null) {
filtersFromElement(elem); filtersFromElement(elem);
showDialog(); showDialog();
return;
}
}
// No mouse position available, use suggested target
var target = details.target || '';
var pos = target.indexOf('\t');
if ( pos === -1 ) {
return;
}
var srcAttrMap = {
'a': 'href',
'img': 'src',
'iframe': 'src',
'embed': 'src',
'video': 'src',
'audio': 'src'
};
var tagName = target.slice(0, pos);
var url = target.slice(pos + 1);
var attr = srcAttrMap[tagName];
if ( attr === undefined ) {
return;
}
var elems = document.querySelectorAll(tagName + '[' + attr + ']');
var i = elems.length;
var src;
while ( i-- ) {
elem = elems[i];
src = elem[attr];
if ( typeof src !== 'string' || src === '' ) {
continue;
}
if ( src !== url ) {
continue;
}
filtersFromElement(elem);
showDialog({ modifier: true });
return;
} }
}; };

View File

@ -574,13 +574,12 @@ var onMessage = function(request, sender, callback) {
callback({ callback({
frameContent: this.responseText.replace(reStrings, replacer), frameContent: this.responseText.replace(reStrings, replacer),
target: µb.contextMenuTarget, targetElementSelector: µb.epickerTargetElementSelector,
clientX: µb.contextMenuClientX, clientX: µb.contextMenuClientX,
clientY: µb.contextMenuClientY, clientY: µb.contextMenuClientY,
eprom: µb.epickerEprom eprom: µb.epickerEprom
}); });
µb.contextMenuTarget = '';
µb.contextMenuClientX = -1; µb.contextMenuClientX = -1;
µb.contextMenuClientY = -1; µb.contextMenuClientY = -1;
}; };

View File

@ -276,8 +276,8 @@ var matchWhitelistDirective = function(url, hostname, directive) {
/******************************************************************************/ /******************************************************************************/
µBlock.elementPickerExec = function(tabId, targetElement) { µBlock.elementPickerExec = function(tabId, targetElementSelector) {
this.epickerTarget = targetElement || ''; this.epickerTargetElementSelector = targetElementSelector;
vAPI.tabs.injectScript(tabId, { file: 'js/element-picker.js' }); vAPI.tabs.injectScript(tabId, { file: 'js/element-picker.js' });
}; };