Merge pull request #136 from tophf/fix-same-openerTabId

Fixes
This commit is contained in:
Jason Barnabe 2015-08-02 21:40:54 -05:00
commit 2319c5c4ac
5 changed files with 141 additions and 81 deletions

View File

@ -1,3 +1,11 @@
var frameIdMessageable;
runTryCatch(function() {
chrome.tabs.sendMessage(0, {}, {frameId: 0}, function() {
var clearError = chrome.runtime.lastError;
frameIdMessageable = true;
});
});
// This happens right away, sometimes so fast that the content script isn't even ready. That's // This happens right away, sometimes so fast that the content script isn't even ready. That's
// why the content script also asks for this stuff. // why the content script also asks for this stuff.
chrome.webNavigation.onCommitted.addListener(webNavigationListener.bind(this, "styleApply")); chrome.webNavigation.onCommitted.addListener(webNavigationListener.bind(this, "styleApply"));
@ -7,15 +15,18 @@ function webNavigationListener(method, data) {
// Until Chrome 41, we can't target a frame with a message // Until Chrome 41, we can't target a frame with a message
// (https://developer.chrome.com/extensions/tabs#method-sendMessage) // (https://developer.chrome.com/extensions/tabs#method-sendMessage)
// so a style affecting a page with an iframe will affect the main page as well. // so a style affecting a page with an iframe will affect the main page as well.
// Skip doing this for frames for now, which can result in flicker. // Skip doing this for frames in pre-41 to prevent page flicker.
if (data.frameId != 0) { if (data.frameId != 0 && !frameIdMessageable) {
return; return;
} }
getStyles({matchUrl: data.url, enabled: true, asHash: true}, function(styleHash) { getStyles({matchUrl: data.url, enabled: true, asHash: true}, function(styleHash) {
if (method) { if (method) {
chrome.tabs.sendMessage(data.tabId, {method: method, styles: styleHash}); chrome.tabs.sendMessage(data.tabId, {method: method, styles: styleHash},
frameIdMessageable ? {frameId: data.frameId} : undefined);
}
if (data.frameId == 0) {
updateIcon({id: data.tabId, url: data.url}, styleHash);
} }
updateIcon({id: data.tabId, url: data.url}, styleHash);
}); });
} }
@ -65,15 +76,17 @@ chrome.commands.onCommand.addListener(function(command) {
// contextMenus API is present in ancient Chrome but it throws an exception // contextMenus API is present in ancient Chrome but it throws an exception
// upon encountering the unsupported parameter value "browser_action", so we have to catch it. // upon encountering the unsupported parameter value "browser_action", so we have to catch it.
runTryCatch(function() {
chrome.contextMenus.create({
id: "show-badge", title: chrome.i18n.getMessage("menuShowBadge"),
type: "checkbox", contexts: ["browser_action"], checked: prefs.getPref("show-badge")
}, function() { var clearError = chrome.runtime.lastError });
chrome.contextMenus.create({
id: "disableAll", title: chrome.i18n.getMessage("disableAllStyles"),
type: "checkbox", contexts: ["browser_action"], checked: prefs.getPref("disableAll")
}, function() { var clearError = chrome.runtime.lastError });
});
chrome.contextMenus.create({
id: "show-badge", title: chrome.i18n.getMessage("menuShowBadge"),
type: "checkbox", contexts: ["browser_action"], checked: prefs.getPref("show-badge")
}, function() { var clearError = chrome.runtime.lastError; });
chrome.contextMenus.create({
id: "disableAll", title: chrome.i18n.getMessage("disableAllStyles"),
type: "checkbox", contexts: ["browser_action"], checked: prefs.getPref("disableAll")
}, function() { var clearError = chrome.runtime.lastError; });
chrome.contextMenus.onClicked.addListener(function(info, tab) { chrome.contextMenus.onClicked.addListener(function(info, tab) {
if (info.menuItemId == "disableAll") { if (info.menuItemId == "disableAll") {
disableAllStylesToggle(info.checked); disableAllStylesToggle(info.checked);
@ -239,13 +252,12 @@ function sectionAppliesToUrl(section, url) {
if (regexp[regexp.length - 1] != "$") { if (regexp[regexp.length - 1] != "$") {
regexp += "$"; regexp += "$";
} }
try { var re = runTryCatch(function() { return new RegExp(regexp) });
var re = new RegExp(regexp); if (re) {
} catch (ex) { return (re).test(url);
} else {
console.log(section.id + "'s regexp '" + regexp + "' is not valid"); console.log(section.id + "'s regexp '" + regexp + "' is not valid");
return false;
} }
return (re).test(url);
})) { })) {
//console.log(section.id + " applies to " + url + " due to regexp rules"); //console.log(section.id + " applies to " + url + " due to regexp rules");
return true; return true;
@ -391,6 +403,13 @@ function openURL(options) {
}); });
} }
// js engine can't optimize the entire function if it contains try-catch
// so we should keep it isolated from normal code in a minimal wrapper
function runTryCatch(func) {
try { return func() }
catch(e) {}
}
var codeMirrorThemes; var codeMirrorThemes;
getCodeMirrorThemes(function(themes) { getCodeMirrorThemes(function(themes) {
codeMirrorThemes = themes; codeMirrorThemes = themes;

145
edit.js
View File

@ -58,10 +58,34 @@ var jumpToLineTemplate = t('editGotoLine') + ': <input class="CodeMirror-jump-fi
NodeList.prototype[method]= Array.prototype[method]; NodeList.prototype[method]= Array.prototype[method];
}); });
// Chrome pre-34
Element.prototype.matches = Element.prototype.matches || Element.prototype.webkitMatchesSelector;
// reroute handling to nearest editor when keypress resolves to one of these commands // reroute handling to nearest editor when keypress resolves to one of these commands
var commandsToReroute = { var hotkeyRerouter = {
save: true, jumpToLine: true, nextEditor: true, prevEditor: true, commands: {
find: true, findNext: true, findPrev: true, replace: true, replaceAll: true save: true, jumpToLine: true, nextEditor: true, prevEditor: true,
find: true, findNext: true, findPrev: true, replace: true, replaceAll: true
},
setState: function(enable) {
setTimeout(function() {
document[(enable ? "add" : "remove") + "EventListener"]("keydown", hotkeyRerouter.eventHandler);
}, 0);
},
eventHandler: function(event) {
var keyName = CodeMirror.keyName(event);
if ("handled" == CodeMirror.lookupKey(keyName, CodeMirror.getOption("keyMap"), handleCommand)
|| "handled" == CodeMirror.lookupKey(keyName, CodeMirror.defaults.extraKeys, handleCommand)) {
event.preventDefault();
event.stopPropagation();
}
function handleCommand(command) {
if (hotkeyRerouter.commands[command] === true) {
CodeMirror.commands[command](getEditorInSight(event.target));
return true;
}
}
}
}; };
function onChange(event) { function onChange(event) {
@ -261,6 +285,8 @@ function initCodeMirror() {
document.getElementById("options").addEventListener("change", acmeEventListener, false); document.getElementById("options").addEventListener("change", acmeEventListener, false);
loadPrefs(controlPrefs); loadPrefs(controlPrefs);
}); });
hotkeyRerouter.setState(true);
} }
initCodeMirror(); initCodeMirror();
@ -313,7 +339,11 @@ function setupCodeMirror(textarea, index) {
var cm = CodeMirror.fromTextArea(textarea); var cm = CodeMirror.fromTextArea(textarea);
cm.on("change", indicateCodeChange); cm.on("change", indicateCodeChange);
cm.on("blur", function(cm) { editors.lastActive = cm }); cm.on("blur", function(cm) {
editors.lastActive = cm;
hotkeyRerouter.setState(true);
});
cm.on("focus", hotkeyRerouter.setState.bind(null, false));
var resizeGrip = cm.display.wrapper.appendChild(document.createElement("div")); var resizeGrip = cm.display.wrapper.appendChild(document.createElement("div"));
resizeGrip.className = "resize-grip"; resizeGrip.className = "resize-grip";
@ -371,26 +401,6 @@ function getCodeMirrorForSection(section) {
return null; return null;
} }
// prevent the browser from seeing hotkeys that should be handled by nearest editor
document.addEventListener("keydown", function(event) {
if (event.target.localName == "textarea") {
return; // let CodeMirror handle it
}
var keyName = CodeMirror.keyName(event);
if ("handled" == CodeMirror.lookupKey(keyName, CodeMirror.getOption("keyMap"), handleCommand)
|| "handled" == CodeMirror.lookupKey(keyName, CodeMirror.defaults.extraKeys, handleCommand)) {
event.preventDefault();
event.stopPropagation();
}
function handleCommand(command) {
if (commandsToReroute[command] === true) {
CodeMirror.commands[command](getEditorInSight(event.target));
return true;
}
}
});
// remind Chrome to repaint a previously invisible editor box by toggling any element's transform // remind Chrome to repaint a previously invisible editor box by toggling any element's transform
// this bug is present in some versions of Chrome (v37-40 or something) // this bug is present in some versions of Chrome (v37-40 or something)
document.addEventListener("scroll", function(event) { document.addEventListener("scroll", function(event) {
@ -451,7 +461,11 @@ window.onbeforeunload = function() {
}); });
} }
document.activeElement.blur(); document.activeElement.blur();
return !isCleanGlobal() ? t('styleChangesNotSaved') : null; if (isCleanGlobal()) {
return;
}
updateLintReport(null, 0);
return confirm(t('styleChangesNotSaved'));
} }
function addAppliesTo(list, name, value) { function addAppliesTo(list, name, value) {
@ -753,24 +767,47 @@ function getEditorInSight(nearbyElement) {
} }
} }
function updateLintReport(cm) { function updateLintReport(cm, delay) {
if (delay == 0) {
// immediately show pending csslint messages in onbeforeunload and save
update.call(cm);
return;
}
if (delay > 0) {
// give csslint some time to find the issues, e.g. 500 (1/10 of our default 5s)
// by settings its internal delay to 1ms and restoring it back later
var lintOpt = editors[0].state.lint.options;
setTimeout((function(opt, delay) {
opt.delay = delay;
update(this);
}).bind(cm, lintOpt, lintOpt.delay), delay);
lintOpt.delay = 1;
return;
}
var state = cm.state.lint; var state = cm.state.lint;
clearTimeout(state.reportTimeout); clearTimeout(state.reportTimeout);
state.reportTimeout = setTimeout(update.bind(cm), (state.options.delay || 500) + 500); state.reportTimeout = setTimeout(update.bind(cm), (state.options.delay || 500) + 4500);
function update() { // this == cm function update() { // this == cm
var html = this.state.lint.marked.length == 0 ? "" : "<tbody>" + var scope = this ? [this] : editors;
this.state.lint.marked.map(function(mark) { var changed = false;
var info = mark.__annotation; scope.forEach(function(cm) {
return "<tr class='" + info.severity + "'>" + var html = cm.state.lint.marked.length == 0 ? "" : "<tbody>" +
"<td role='severity' class='CodeMirror-lint-marker-" + info.severity + "'>" + cm.state.lint.marked.map(function(mark) {
info.severity + "</td>" + var info = mark.__annotation;
"<td role='line'>" + (info.from.line+1) + "</td>" + return "<tr class='" + info.severity + "'>" +
"<td role='sep'>:</td>" + "<td role='severity' class='CodeMirror-lint-marker-" + info.severity + "'>" +
"<td role='col'>" + (info.from.ch+1) + "</td>" + info.severity + "</td>" +
"<td role='message'>" + info.message.replace(/ at line \d.+$/, "") + "</td></tr>"; "<td role='line'>" + (info.from.line+1) + "</td>" +
}).join("") + "</tbody>"; "<td role='sep'>:</td>" +
if (this.state.lint.html != html) { "<td role='col'>" + (info.from.ch+1) + "</td>" +
this.state.lint.html = html; "<td role='message'>" + info.message.replace(/ at line \d.+$/, "") + "</td></tr>";
}).join("") + "</tbody>";
if (cm.state.lint.html != html) {
cm.state.lint.html = html;
changed = true;
}
});
if (changed) {
renderLintReport(true); renderLintReport(true);
} }
} }
@ -949,19 +986,23 @@ function initWithStyle(style) {
}); });
var queue = style.sections.length ? style.sections : [{code: ""}]; var queue = style.sections.length ? style.sections : [{code: ""}];
var queueStart = new Date().getTime(); var queueStart = new Date().getTime();
// after 200ms the sections will be added asynchronously // after 100ms the sections will be added asynchronously
while (new Date().getTime() - queueStart <= 200 && queue.length) { while (new Date().getTime() - queueStart <= 100 && queue.length) {
maximizeCodeHeight(addSection(null, queue.shift()), !queue.length); add();
}
if (queue.length) {
setTimeout(function processQueue() {
maximizeCodeHeight(addSection(null, queue.shift()), !queue.length);
if (queue.length) {
setTimeout(processQueue, 0);
}
}, 0);
} }
(function processQueue() {
if (queue.length) {
add();
setTimeout(processQueue, 0);
}
})();
initHooks(); initHooks();
function add() {
var sectionDiv = addSection(null, queue.shift());
maximizeCodeHeight(sectionDiv, !queue.length);
updateLintReport(getCodeMirrorForSection(sectionDiv), 500);
}
} }
function initHooks() { function initHooks() {
@ -1071,6 +1112,8 @@ function validate() {
} }
function save() { function save() {
updateLintReport(null, 0);
// save the contents of the CodeMirror editors back into the textareas // save the contents of the CodeMirror editors back into the textareas
for (var i=0; i < editors.length; i++) { for (var i=0; i < editors.length; i++) {
editors[i].save(); editors[i].save();

View File

@ -35,13 +35,14 @@ function tNodeList(nodes) {
if (node.nodeType != 1) { // not an ELEMENT_NODE if (node.nodeType != 1) { // not an ELEMENT_NODE
continue; continue;
} }
for (var a = 0; a < node.attributes.length; a++) { for (var a = node.attributes.length - 1; a >= 0; a--) {
var name = node.attributes[a].nodeName; var attr = node.attributes[a];
var name = attr.nodeName;
if (name.indexOf("i18n-") != 0) { if (name.indexOf("i18n-") != 0) {
continue; continue;
} }
name = name.substr(5); // "i18n-".length name = name.substr(5); // "i18n-".length
var value = t(node.attributes[a].nodeValue); var value = t(attr.nodeValue);
switch (name) { switch (name) {
case "text": case "text":
node.insertBefore(document.createTextNode(value), node.firstChild); node.insertBefore(document.createTextNode(value), node.firstChild);
@ -52,6 +53,7 @@ function tNodeList(nodes) {
default: default:
node.setAttribute(name, value); node.setAttribute(name, value);
} }
node.removeAttributeNode(attr);
} }
} }
} }
@ -69,5 +71,6 @@ function tDocLoader() {
observer.observe(document, {subtree: true, childList: true}); observer.observe(document, {subtree: true, childList: true});
document.addEventListener("DOMContentLoaded", function() { document.addEventListener("DOMContentLoaded", function() {
observer.disconnect(); observer.disconnect();
tNodeList(document.querySelectorAll("*"));
}); });
} }

View File

@ -76,14 +76,9 @@ function getActiveTabRealURL(callback) {
function getTabRealURL(tab, callback) { function getTabRealURL(tab, callback) {
if (tab.url != "chrome://newtab/") { if (tab.url != "chrome://newtab/") {
callback(tab.url); callback(tab.url);
return; } else {
} chrome.webNavigation.getFrame({tabId: tab.id, frameId: 0, processId: -1}, function(frame) {
chrome.webNavigation.getAllFrames({tabId: tab.id}, function(frames) { frame && callback(frame.url);
frames.some(function(frame) {
if (frame.parentFrameId == -1) { // parentless frame is the main frame
callback(frame.url);
return true;
}
}); });
}); }
} }

View File

@ -271,7 +271,7 @@ function shallowCopy(obj) {
} }
function equal(a, b) { function equal(a, b) {
if (!a || !b || typeof a != "object") { if (!a || !b || typeof a != "object" || typeof b != "object") {
return a === b; return a === b;
} }
if (Object.keys(a).length != Object.keys(b).length) { if (Object.keys(a).length != Object.keys(b).length) {