Switch to IndexedDB #167
This commit is contained in:
parent
ddb6f400e7
commit
2973cac28f
4
apply.js
4
apply.js
|
@ -29,7 +29,7 @@ chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
|
||||||
removeStyle(request.id, document);
|
removeStyle(request.id, document);
|
||||||
break;
|
break;
|
||||||
case "styleUpdated":
|
case "styleUpdated":
|
||||||
if (request.style.enabled == "true") {
|
if (request.style.enabled) {
|
||||||
retireStyle(request.style.id);
|
retireStyle(request.style.id);
|
||||||
// fallthrough to "styleAdded"
|
// fallthrough to "styleAdded"
|
||||||
} else {
|
} else {
|
||||||
|
@ -37,7 +37,7 @@ chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "styleAdded":
|
case "styleAdded":
|
||||||
if (request.style.enabled == "true") {
|
if (request.style.enabled) {
|
||||||
chrome.runtime.sendMessage({method: "getStyles", matchUrl: location.href, enabled: true, id: request.style.id, asHash: true}, applyStyles);
|
chrome.runtime.sendMessage({method: "getStyles", matchUrl: location.href, enabled: true, id: request.style.id, asHash: true}, applyStyles);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
283
background.js
283
background.js
|
@ -66,9 +66,6 @@ chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
|
||||||
case "saveStyle":
|
case "saveStyle":
|
||||||
saveStyle(request, sendResponse);
|
saveStyle(request, sendResponse);
|
||||||
return true;
|
return true;
|
||||||
case "styleChanged":
|
|
||||||
cachedStyles = null;
|
|
||||||
break;
|
|
||||||
case "healthCheck":
|
case "healthCheck":
|
||||||
getDatabase(function() { sendResponse(true); }, function() { sendResponse(false); });
|
getDatabase(function() { sendResponse(true); }, function() { sendResponse(false); });
|
||||||
break;
|
break;
|
||||||
|
@ -126,279 +123,6 @@ function disableAllStylesToggle(newState) {
|
||||||
prefs.set("disableAll", newState);
|
prefs.set("disableAll", newState);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStyles(options, callback) {
|
|
||||||
|
|
||||||
var enabled = fixBoolean(options.enabled);
|
|
||||||
var url = "url" in options ? options.url : null;
|
|
||||||
var id = "id" in options ? options.id : null;
|
|
||||||
var matchUrl = "matchUrl" in options ? options.matchUrl : null;
|
|
||||||
// Return as a hash from style to applicable sections? Can only be used with matchUrl.
|
|
||||||
var asHash = "asHash" in options ? options.asHash : false;
|
|
||||||
|
|
||||||
var callCallback = function() {
|
|
||||||
var styles = asHash ? {disableAll: prefs.get("disableAll", false)} : [];
|
|
||||||
cachedStyles.forEach(function(style) {
|
|
||||||
if (enabled != null && fixBoolean(style.enabled) != enabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (url != null && style.url != url) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (id != null && style.id != id) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (matchUrl != null) {
|
|
||||||
var applicableSections = getApplicableSections(style, matchUrl);
|
|
||||||
if (applicableSections.length > 0) {
|
|
||||||
if (asHash) {
|
|
||||||
styles[style.id] = applicableSections;
|
|
||||||
} else {
|
|
||||||
styles.push(style)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
styles.push(style);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
callback(styles);
|
|
||||||
return styles;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cachedStyles) {
|
|
||||||
return callCallback();
|
|
||||||
}
|
|
||||||
|
|
||||||
getDatabase(function(db) {
|
|
||||||
db.readTransaction(function (t) {
|
|
||||||
var where = "";
|
|
||||||
var params = [];
|
|
||||||
|
|
||||||
t.executeSql('SELECT DISTINCT s.*, se.id section_id, se.code, sm.name metaName, sm.value metaValue FROM styles s LEFT JOIN sections se ON se.style_id = s.id LEFT JOIN section_meta sm ON sm.section_id = se.id WHERE 1' + where + ' ORDER BY s.id, se.id, sm.id', params, function (t, r) {
|
|
||||||
cachedStyles = [];
|
|
||||||
var currentStyle = null;
|
|
||||||
var currentSection = null;
|
|
||||||
for (var i = 0; i < r.rows.length; i++) {
|
|
||||||
var values = r.rows.item(i);
|
|
||||||
var metaName = null;
|
|
||||||
switch (values.metaName) {
|
|
||||||
case null:
|
|
||||||
break;
|
|
||||||
case "url":
|
|
||||||
metaName = "urls";
|
|
||||||
break;
|
|
||||||
case "url-prefix":
|
|
||||||
metaName = "urlPrefixes";
|
|
||||||
break;
|
|
||||||
case "domain":
|
|
||||||
var metaName = "domains";
|
|
||||||
break;
|
|
||||||
case "regexps":
|
|
||||||
var metaName = "regexps";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
var metaName = values.metaName + "s";
|
|
||||||
}
|
|
||||||
var metaValue = values.metaValue;
|
|
||||||
if (currentStyle == null || currentStyle.id != values.id) {
|
|
||||||
currentStyle = {id: values.id, url: values.url, updateUrl: values.updateUrl, md5Url: values.md5Url, name: values.name, enabled: values.enabled, originalMd5: values.originalMd5, sections: []};
|
|
||||||
cachedStyles.push(currentStyle);
|
|
||||||
}
|
|
||||||
if (values.section_id != null) {
|
|
||||||
if (currentSection == null || currentSection.id != values.section_id) {
|
|
||||||
currentSection = {id: values.section_id, code: values.code};
|
|
||||||
currentStyle.sections.push(currentSection);
|
|
||||||
}
|
|
||||||
if (metaName && metaValue) {
|
|
||||||
if (currentSection[metaName]) {
|
|
||||||
currentSection[metaName].push(metaValue);
|
|
||||||
} else {
|
|
||||||
currentSection[metaName] = [metaValue];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
callCallback();
|
|
||||||
}, reportError);
|
|
||||||
}, reportError);
|
|
||||||
}, reportError);
|
|
||||||
}
|
|
||||||
|
|
||||||
function fixBoolean(b) {
|
|
||||||
if (typeof b != "undefined") {
|
|
||||||
return b != "false";
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var namespacePattern = /^\s*(@namespace[^;]+;\s*)+$/;
|
|
||||||
function getApplicableSections(style, url) {
|
|
||||||
var sections = style.sections.filter(function(section) {
|
|
||||||
return sectionAppliesToUrl(section, url);
|
|
||||||
});
|
|
||||||
// ignore if it's just namespaces
|
|
||||||
if (sections.length == 1 && namespacePattern.test(sections[0].code)) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
return sections;
|
|
||||||
}
|
|
||||||
|
|
||||||
function sectionAppliesToUrl(section, url) {
|
|
||||||
// only http, https, file, and chrome-extension allowed
|
|
||||||
if (url.indexOf("http") != 0 && url.indexOf("file") != 0 && url.indexOf("chrome-extension") != 0 && url.indexOf("ftp") != 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// other extensions can't be styled
|
|
||||||
if (url.indexOf("chrome-extension") == 0 && url.indexOf(chrome.extension.getURL("")) != 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!section.urls && !section.domains && !section.urlPrefixes && !section.regexps) {
|
|
||||||
//console.log(section.id + " is global");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (section.urls && section.urls.indexOf(url) != -1) {
|
|
||||||
//console.log(section.id + " applies to " + url + " due to URL rules");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (section.urlPrefixes && section.urlPrefixes.some(function(prefix) {
|
|
||||||
return url.indexOf(prefix) == 0;
|
|
||||||
})) {
|
|
||||||
//console.log(section.id + " applies to " + url + " due to URL prefix rules");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (section.domains && getDomains(url).some(function(domain) {
|
|
||||||
return section.domains.indexOf(domain) != -1;
|
|
||||||
})) {
|
|
||||||
//console.log(section.id + " applies due to " + url + " due to domain rules");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (section.regexps && section.regexps.some(function(regexp) {
|
|
||||||
// we want to match the full url, so add ^ and $ if not already present
|
|
||||||
if (regexp[0] != "^") {
|
|
||||||
regexp = "^" + regexp;
|
|
||||||
}
|
|
||||||
if (regexp[regexp.length - 1] != "$") {
|
|
||||||
regexp += "$";
|
|
||||||
}
|
|
||||||
var re = runTryCatch(function() { return new RegExp(regexp) });
|
|
||||||
if (re) {
|
|
||||||
return (re).test(url);
|
|
||||||
} else {
|
|
||||||
console.log(section.id + "'s regexp '" + regexp + "' is not valid");
|
|
||||||
}
|
|
||||||
})) {
|
|
||||||
//console.log(section.id + " applies to " + url + " due to regexp rules");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
//console.log(section.id + " does not apply due to " + url);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var cachedStyles = null;
|
|
||||||
|
|
||||||
function saveStyle(o, callback) {
|
|
||||||
getDatabase(function(db) {
|
|
||||||
db.transaction(function(t) {
|
|
||||||
if (o.id) {
|
|
||||||
// update whatever's been passed
|
|
||||||
if ("name" in o) {
|
|
||||||
t.executeSql('UPDATE styles SET name = ? WHERE id = ?;', [o.name, o.id]);
|
|
||||||
}
|
|
||||||
if ("enabled" in o) {
|
|
||||||
t.executeSql('UPDATE styles SET enabled = ? WHERE id = ?;', [o.enabled, o.id]);
|
|
||||||
}
|
|
||||||
if ("url" in o) {
|
|
||||||
t.executeSql('UPDATE styles SET url = ? WHERE id = ?;', [o.url, o.id]);
|
|
||||||
}
|
|
||||||
if ("updateUrl" in o) {
|
|
||||||
t.executeSql('UPDATE styles SET updateUrl = ? WHERE id = ?;', [o.updateUrl, o.id]);
|
|
||||||
}
|
|
||||||
if ("md5Url" in o) {
|
|
||||||
t.executeSql('UPDATE styles SET md5Url = ? WHERE id = ?;', [o.md5Url, o.id]);
|
|
||||||
}
|
|
||||||
if ("originalMd5" in o) {
|
|
||||||
t.executeSql('UPDATE styles SET originalMd5 = ? WHERE id = ?;', [o.originalMd5, o.id]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// create a new record
|
|
||||||
// set optional things to null if they're undefined
|
|
||||||
["updateUrl", "md5Url", "url", "originalMd5"].filter(function(att) {
|
|
||||||
return !(att in o);
|
|
||||||
}).forEach(function(att) {
|
|
||||||
o[att] = null;
|
|
||||||
});
|
|
||||||
t.executeSql('INSERT INTO styles (name, enabled, url, updateUrl, md5Url, originalMd5) VALUES (?, ?, ?, ?, ?, ?);', [o.name, true, o.url, o.updateUrl, o.md5Url, o.originalMd5]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ("sections" in o) {
|
|
||||||
if (o.id) {
|
|
||||||
// clear existing records
|
|
||||||
t.executeSql('DELETE FROM section_meta WHERE section_id IN (SELECT id FROM sections WHERE style_id = ?);', [o.id]);
|
|
||||||
t.executeSql('DELETE FROM sections WHERE style_id = ?;', [o.id]);
|
|
||||||
}
|
|
||||||
|
|
||||||
o.sections.forEach(function(section) {
|
|
||||||
if (o.id) {
|
|
||||||
t.executeSql('INSERT INTO sections (style_id, code) VALUES (?, ?);', [o.id, section.code]);
|
|
||||||
} else {
|
|
||||||
t.executeSql('INSERT INTO sections (style_id, code) SELECT id, ? FROM styles ORDER BY id DESC LIMIT 1;', [section.code]);
|
|
||||||
}
|
|
||||||
if (section.urls) {
|
|
||||||
section.urls.forEach(function(u) {
|
|
||||||
t.executeSql("INSERT INTO section_meta (section_id, name, value) SELECT id, 'url', ? FROM sections ORDER BY id DESC LIMIT 1;", [u]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (section.urlPrefixes) {
|
|
||||||
section.urlPrefixes.forEach(function(u) {
|
|
||||||
t.executeSql("INSERT INTO section_meta (section_id, name, value) SELECT id, 'url-prefix', ? FROM sections ORDER BY id DESC LIMIT 1;", [u]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (section.domains) {
|
|
||||||
section.domains.forEach(function(u) {
|
|
||||||
t.executeSql("INSERT INTO section_meta (section_id, name, value) SELECT id, 'domain', ? FROM sections ORDER BY id DESC LIMIT 1;", [u]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (section.regexps) {
|
|
||||||
section.regexps.forEach(function(u) {
|
|
||||||
t.executeSql("INSERT INTO section_meta (section_id, name, value) SELECT id, 'regexp', ? FROM sections ORDER BY id DESC LIMIT 1;", [u]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, reportError, function() {saveFromJSONComplete(o.id, callback)});
|
|
||||||
}, reportError);
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveFromJSONComplete(id, callback) {
|
|
||||||
cachedStyles = null;
|
|
||||||
|
|
||||||
if (id) {
|
|
||||||
getStyles({method: "getStyles", id: id}, function(styles) {
|
|
||||||
saveFromJSONStyleReloaded("styleUpdated", styles[0], callback);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we need to load the id for new ones
|
|
||||||
getDatabase(function(db) {
|
|
||||||
db.readTransaction(function (t) {
|
|
||||||
t.executeSql('SELECT id FROM styles ORDER BY id DESC LIMIT 1', [], function(t, r) {
|
|
||||||
var id = r.rows.item(0).id;
|
|
||||||
getStyles({method: "getStyles", id: id}, function(styles) {
|
|
||||||
saveFromJSONStyleReloaded("styleAdded", styles[0], callback);
|
|
||||||
});
|
|
||||||
}, reportError)
|
|
||||||
}, reportError)
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveFromJSONStyleReloaded(updateType, style, callback) {
|
|
||||||
notifyAllTabs({method: updateType, style: style});
|
|
||||||
if (callback) {
|
|
||||||
callback(style);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the DB so that any first run actions will be performed immediately when the background page loads.
|
// Get the DB so that any first run actions will be performed immediately when the background page loads.
|
||||||
getDatabase(function() {}, reportError);
|
getDatabase(function() {}, reportError);
|
||||||
|
|
||||||
|
@ -430,13 +154,6 @@ 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;
|
||||||
|
|
2
edit.js
2
edit.js
|
@ -1093,7 +1093,7 @@ function init() {
|
||||||
|
|
||||||
function initWithStyle(style) {
|
function initWithStyle(style) {
|
||||||
document.getElementById("name").value = style.name;
|
document.getElementById("name").value = style.name;
|
||||||
document.getElementById("enabled").checked = style.enabled == "true";
|
document.getElementById("enabled").checked = style.enabled;
|
||||||
document.getElementById("url").href = style.url;
|
document.getElementById("url").href = style.url;
|
||||||
// if this was done in response to an update, we need to clear existing sections
|
// if this was done in response to an update, we need to clear existing sections
|
||||||
getSections().forEach(function(div) { div.remove(); });
|
getSections().forEach(function(div) { div.remove(); });
|
||||||
|
|
|
@ -29,7 +29,7 @@ function showStyles(styles) {
|
||||||
|
|
||||||
function createStyleElement(style) {
|
function createStyleElement(style) {
|
||||||
var e = template.style.cloneNode(true);
|
var e = template.style.cloneNode(true);
|
||||||
e.setAttribute("class", style.enabled == "true" ? "enabled" : "disabled");
|
e.setAttribute("class", style.enabled ? "enabled" : "disabled");
|
||||||
e.setAttribute("style-id", style.id);
|
e.setAttribute("style-id", style.id);
|
||||||
if (style.updateUrl) {
|
if (style.updateUrl) {
|
||||||
e.setAttribute("style-update-url", style.updateUrl);
|
e.setAttribute("style-update-url", style.updateUrl);
|
||||||
|
@ -190,7 +190,10 @@ function handleUpdate(style) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDelete(id) {
|
function handleDelete(id) {
|
||||||
installed.removeChild(installed.querySelector("[style-id='" + id + "']"));
|
var node = installed.querySelector("[style-id='" + id + "']");
|
||||||
|
if (node) {
|
||||||
|
installed.removeChild(node);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function doCheckUpdate(event) {
|
function doCheckUpdate(event) {
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
"https://userstyles.org/"
|
"https://userstyles.org/"
|
||||||
],
|
],
|
||||||
"background": {
|
"background": {
|
||||||
"scripts": ["messaging.js", "storage.js", "background.js"]
|
"scripts": ["messaging.js", "storage-websql.js", "storage.js", "background.js"]
|
||||||
},
|
},
|
||||||
"commands": {
|
"commands": {
|
||||||
"openManage": {
|
"openManage": {
|
||||||
|
|
4
popup.js
4
popup.js
|
@ -88,9 +88,9 @@ function createStyleElement(style) {
|
||||||
var e = template.style.cloneNode(true);
|
var e = template.style.cloneNode(true);
|
||||||
var checkbox = e.querySelector(".checker");
|
var checkbox = e.querySelector(".checker");
|
||||||
checkbox.id = "style-" + style.id;
|
checkbox.id = "style-" + style.id;
|
||||||
checkbox.checked = style.enabled == "true";
|
checkbox.checked = style.enabled;
|
||||||
|
|
||||||
e.setAttribute("class", "entry " + (style.enabled == "true" ? "enabled" : "disabled"));
|
e.setAttribute("class", "entry " + (style.enabled ? "enabled" : "disabled"));
|
||||||
e.setAttribute("style-id", style.id);
|
e.setAttribute("style-id", style.id);
|
||||||
var styleName = e.querySelector(".style-name");
|
var styleName = e.querySelector(".style-name");
|
||||||
styleName.appendChild(document.createTextNode(style.name));
|
styleName.appendChild(document.createTextNode(style.name));
|
||||||
|
|
152
storage-websql.js
Normal file
152
storage-websql.js
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
var webSqlStorage = {
|
||||||
|
|
||||||
|
migrate: function() {
|
||||||
|
webSqlStorage.getStyles(function(styles) {
|
||||||
|
getDatabase(function(db) {
|
||||||
|
var tx = db.transaction(["styles"], "readwrite");
|
||||||
|
var os = tx.objectStore("styles");
|
||||||
|
styles.forEach(function(s) {
|
||||||
|
webSqlStorage.cleanStyle(s)
|
||||||
|
os.add(s);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, null);
|
||||||
|
},
|
||||||
|
|
||||||
|
cleanStyle: function(s) {
|
||||||
|
delete s.id;
|
||||||
|
s.sections.forEach(function(section) {
|
||||||
|
delete section.id;
|
||||||
|
["urls", "urlPrefixes", "domains", "regexps"].forEach(function(property) {
|
||||||
|
if (!section[property]) {
|
||||||
|
section[property] = [];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getStyles: function(callback) {
|
||||||
|
webSqlStorage.getDatabase(function(db) {
|
||||||
|
db.readTransaction(function (t) {
|
||||||
|
var where = "";
|
||||||
|
var params = [];
|
||||||
|
|
||||||
|
t.executeSql('SELECT DISTINCT s.*, se.id section_id, se.code, sm.name metaName, sm.value metaValue FROM styles s LEFT JOIN sections se ON se.style_id = s.id LEFT JOIN section_meta sm ON sm.section_id = se.id WHERE 1' + where + ' ORDER BY s.id, se.id, sm.id', params, function (t, r) {
|
||||||
|
var styles = [];
|
||||||
|
var currentStyle = null;
|
||||||
|
var currentSection = null;
|
||||||
|
for (var i = 0; i < r.rows.length; i++) {
|
||||||
|
var values = r.rows.item(i);
|
||||||
|
var metaName = null;
|
||||||
|
switch (values.metaName) {
|
||||||
|
case null:
|
||||||
|
break;
|
||||||
|
case "url":
|
||||||
|
metaName = "urls";
|
||||||
|
break;
|
||||||
|
case "url-prefix":
|
||||||
|
metaName = "urlPrefixes";
|
||||||
|
break;
|
||||||
|
case "domain":
|
||||||
|
var metaName = "domains";
|
||||||
|
break;
|
||||||
|
case "regexps":
|
||||||
|
var metaName = "regexps";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
var metaName = values.metaName + "s";
|
||||||
|
}
|
||||||
|
var metaValue = values.metaValue;
|
||||||
|
if (currentStyle == null || currentStyle.id != values.id) {
|
||||||
|
currentStyle = {id: values.id, url: values.url, updateUrl: values.updateUrl, md5Url: values.md5Url, name: values.name, enabled: values.enabled == "true", originalMd5: values.originalMd5, sections: []};
|
||||||
|
styles.push(currentStyle);
|
||||||
|
}
|
||||||
|
if (values.section_id != null) {
|
||||||
|
if (currentSection == null || currentSection.id != values.section_id) {
|
||||||
|
currentSection = {id: values.section_id, code: values.code};
|
||||||
|
currentStyle.sections.push(currentSection);
|
||||||
|
}
|
||||||
|
if (metaName && metaValue) {
|
||||||
|
if (currentSection[metaName]) {
|
||||||
|
currentSection[metaName].push(metaValue);
|
||||||
|
} else {
|
||||||
|
currentSection[metaName] = [metaValue];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callback(styles);
|
||||||
|
}, reportError);
|
||||||
|
}, reportError);
|
||||||
|
}, reportError);
|
||||||
|
},
|
||||||
|
|
||||||
|
getDatabase: function(ready, error) {
|
||||||
|
try {
|
||||||
|
stylishDb = openDatabase('stylish', '', 'Stylish Styles', 5*1024*1024);
|
||||||
|
} catch (ex) {
|
||||||
|
error();
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
if (stylishDb.version == "1.0" || stylishDb.version == "") {
|
||||||
|
webSqlStorage.dbV11(stylishDb, error, ready);
|
||||||
|
} else if (stylishDb.version == "1.1") {
|
||||||
|
webSqlStorage.dbV12(stylishDb, error, ready);
|
||||||
|
} else if (stylishDb.version == "1.2") {
|
||||||
|
webSqlStorage.dbV13(stylishDb, error, ready);
|
||||||
|
} else if (stylishDb.version == "1.3") {
|
||||||
|
webSqlStorage.dbV14(stylishDb, error, ready);
|
||||||
|
} else if (stylishDb.version == "1.4") {
|
||||||
|
webSqlStorage.dbV15(stylishDb, error, ready);
|
||||||
|
} else {
|
||||||
|
ready(stylishDb);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
dbV11: function(d, error, done) {
|
||||||
|
d.changeVersion(d.version, '1.1', function (t) {
|
||||||
|
t.executeSql('CREATE TABLE styles (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, url TEXT, updateUrl TEXT, md5Url TEXT, name TEXT NOT NULL, code TEXT NOT NULL, enabled INTEGER NOT NULL, originalCode TEXT NULL);');
|
||||||
|
t.executeSql('CREATE TABLE style_meta (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, style_id INTEGER NOT NULL, name TEXT NOT NULL, value TEXT NOT NULL);');
|
||||||
|
t.executeSql('CREATE INDEX style_meta_style_id ON style_meta (style_id);');
|
||||||
|
}, error, function() { webSqlStorage.dbV12(d, error, done)});
|
||||||
|
},
|
||||||
|
|
||||||
|
dbV12: function(d, error, done) {
|
||||||
|
d.changeVersion(d.version, '1.2', function (t) {
|
||||||
|
// add section table
|
||||||
|
t.executeSql('CREATE TABLE sections (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, style_id INTEGER NOT NULL, code TEXT NOT NULL);');
|
||||||
|
t.executeSql('INSERT INTO sections (style_id, code) SELECT id, code FROM styles;');
|
||||||
|
// switch meta to sections
|
||||||
|
t.executeSql('DROP INDEX style_meta_style_id;');
|
||||||
|
t.executeSql('CREATE TABLE section_meta (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, section_id INTEGER NOT NULL, name TEXT NOT NULL, value TEXT NOT NULL);');
|
||||||
|
t.executeSql('INSERT INTO section_meta (section_id, name, value) SELECT s.id, sm.name, sm.value FROM sections s INNER JOIN style_meta sm ON sm.style_id = s.style_id;');
|
||||||
|
t.executeSql('CREATE INDEX section_meta_section_id ON section_meta (section_id);');
|
||||||
|
t.executeSql('DROP TABLE style_meta;');
|
||||||
|
// drop extra fields from styles table
|
||||||
|
t.executeSql('CREATE TABLE newstyles (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, url TEXT, updateUrl TEXT, md5Url TEXT, name TEXT NOT NULL, enabled INTEGER NOT NULL);');
|
||||||
|
t.executeSql('INSERT INTO newstyles (id, url, updateUrl, md5Url, name, enabled) SELECT id, url, updateUrl, md5Url, name, enabled FROM styles;');
|
||||||
|
t.executeSql('DROP TABLE styles;');
|
||||||
|
t.executeSql('ALTER TABLE newstyles RENAME TO styles;');
|
||||||
|
}, error, function() { webSqlStorage.dbV13(d, error, done)});
|
||||||
|
},
|
||||||
|
|
||||||
|
dbV13: function(d, error, done) {
|
||||||
|
d.changeVersion(d.version, '1.3', function (t) {
|
||||||
|
// clear out orphans
|
||||||
|
t.executeSql('DELETE FROM section_meta WHERE section_id IN (SELECT sections.id FROM sections LEFT JOIN styles ON styles.id = sections.style_id WHERE styles.id IS NULL);');
|
||||||
|
t.executeSql('DELETE FROM sections WHERE id IN (SELECT sections.id FROM sections LEFT JOIN styles ON styles.id = sections.style_id WHERE styles.id IS NULL);');
|
||||||
|
}, error, function() { webSqlStorage.dbV14(d, error, done)});
|
||||||
|
},
|
||||||
|
|
||||||
|
dbV14: function(d, error, done) {
|
||||||
|
d.changeVersion(d.version, '1.4', function (t) {
|
||||||
|
t.executeSql('UPDATE styles SET url = null WHERE url = "undefined";');
|
||||||
|
}, error, function() { webSqlStorage.dbV15(d, error, done)});
|
||||||
|
},
|
||||||
|
|
||||||
|
dbV15: function(d, error, done) {
|
||||||
|
d.changeVersion(d.version, '1.5', function (t) {
|
||||||
|
t.executeSql('ALTER TABLE styles ADD COLUMN originalMd5 TEXT NULL;');
|
||||||
|
}, error, function() { done(d); });
|
||||||
|
}
|
||||||
|
}
|
277
storage.js
277
storage.js
|
@ -1,102 +1,144 @@
|
||||||
var stylishDb = null;
|
|
||||||
function getDatabase(ready, error) {
|
function getDatabase(ready, error) {
|
||||||
if (stylishDb != null && stylishDb.version == "1.5") {
|
var dbOpenRequest = window.indexedDB.open("stylish", 2);
|
||||||
ready(stylishDb);
|
dbOpenRequest.onsuccess = function(e) {
|
||||||
|
ready(e.target.result);
|
||||||
|
};
|
||||||
|
dbOpenRequest.onerror = function(event) {
|
||||||
|
console.log(event.target.errorCode);
|
||||||
|
if (error) {
|
||||||
|
error(event);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
dbOpenRequest.onupgradeneeded = function(event) {
|
||||||
|
if (event.oldVersion == 0) {
|
||||||
|
var os = event.target.result.createObjectStore("styles", {keyPath: 'id', autoIncrement: true});
|
||||||
|
webSqlStorage.migrate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function getStyles(options, callback) {
|
||||||
|
getDatabase(function(db) {
|
||||||
|
var tx = db.transaction(["styles"], "readonly");
|
||||||
|
var os = tx.objectStore("styles");
|
||||||
|
var all = [];
|
||||||
|
os.openCursor().onsuccess = function(event) {
|
||||||
|
var cursor = event.target.result;
|
||||||
|
if (cursor) {
|
||||||
|
var s = cursor.value
|
||||||
|
s.id = cursor.key
|
||||||
|
all.push(cursor.value);
|
||||||
|
cursor.continue();
|
||||||
|
} else {
|
||||||
|
callback(filterStyles(all, options));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterStyles(unfilteredStyles, options) {
|
||||||
|
var enabled = fixBoolean(options.enabled);
|
||||||
|
var url = "url" in options ? options.url : null;
|
||||||
|
var id = "id" in options ? Number(options.id) : null;
|
||||||
|
var matchUrl = "matchUrl" in options ? options.matchUrl : null;
|
||||||
|
// Return as a hash from style to applicable sections? Can only be used with matchUrl.
|
||||||
|
var asHash = "asHash" in options ? options.asHash : false;
|
||||||
|
|
||||||
|
var styles = asHash ? {disableAll: prefs.get("disableAll", false)} : [];
|
||||||
|
unfilteredStyles.forEach(function(style) {
|
||||||
|
if (enabled != null && style.enabled != enabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
if (url != null && style.url != url) {
|
||||||
stylishDb = openDatabase('stylish', '', 'Stylish Styles', 5*1024*1024);
|
return;
|
||||||
} catch (ex) {
|
|
||||||
error();
|
|
||||||
throw ex;
|
|
||||||
}
|
}
|
||||||
if (stylishDb.version == "1.0" || stylishDb.version == "") {
|
if (id != null && style.id != id) {
|
||||||
dbV11(stylishDb, error, ready);
|
return;
|
||||||
} else if (stylishDb.version == "1.1") {
|
}
|
||||||
dbV12(stylishDb, error, ready);
|
if (matchUrl != null) {
|
||||||
} else if (stylishDb.version == "1.2") {
|
var applicableSections = getApplicableSections(style, matchUrl);
|
||||||
dbV13(stylishDb, error, ready);
|
if (applicableSections.length > 0) {
|
||||||
} else if (stylishDb.version == "1.3") {
|
if (asHash) {
|
||||||
dbV14(stylishDb, error, ready);
|
styles[style.id] = applicableSections;
|
||||||
} else if (stylishDb.version == "1.4") {
|
|
||||||
dbV15(stylishDb, error, ready);
|
|
||||||
} else {
|
} else {
|
||||||
ready(stylishDb);
|
styles.push(style)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
styles.push(style);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return styles;
|
||||||
}
|
}
|
||||||
|
|
||||||
function dbV11(d, error, done) {
|
function saveStyle(o, callback) {
|
||||||
d.changeVersion(d.version, '1.1', function (t) {
|
getDatabase(function(db) {
|
||||||
t.executeSql('CREATE TABLE styles (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, url TEXT, updateUrl TEXT, md5Url TEXT, name TEXT NOT NULL, code TEXT NOT NULL, enabled INTEGER NOT NULL, originalCode TEXT NULL);');
|
var tx = db.transaction(["styles"], "readwrite");
|
||||||
t.executeSql('CREATE TABLE style_meta (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, style_id INTEGER NOT NULL, name TEXT NOT NULL, value TEXT NOT NULL);');
|
var os = tx.objectStore("styles");
|
||||||
t.executeSql('CREATE INDEX style_meta_style_id ON style_meta (style_id);');
|
|
||||||
}, error, function() {dbV12(d, error, done)});
|
|
||||||
}
|
|
||||||
|
|
||||||
function dbV12(d, error, done) {
|
// Update
|
||||||
d.changeVersion(d.version, '1.2', function (t) {
|
if (o.id) {
|
||||||
// add section table
|
var request = os.get(Number(o.id));
|
||||||
t.executeSql('CREATE TABLE sections (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, style_id INTEGER NOT NULL, code TEXT NOT NULL);');
|
request.onsuccess = function(event) {
|
||||||
t.executeSql('INSERT INTO sections (style_id, code) SELECT id, code FROM styles;');
|
var style = request.result;
|
||||||
// switch meta to sections
|
for (var prop in o) {
|
||||||
t.executeSql('DROP INDEX style_meta_style_id;');
|
if (prop == "id") {
|
||||||
t.executeSql('CREATE TABLE section_meta (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, section_id INTEGER NOT NULL, name TEXT NOT NULL, value TEXT NOT NULL);');
|
continue;
|
||||||
t.executeSql('INSERT INTO section_meta (section_id, name, value) SELECT s.id, sm.name, sm.value FROM sections s INNER JOIN style_meta sm ON sm.style_id = s.style_id;');
|
}
|
||||||
t.executeSql('CREATE INDEX section_meta_section_id ON section_meta (section_id);');
|
style[prop] = o[prop];
|
||||||
t.executeSql('DROP TABLE style_meta;');
|
}
|
||||||
// drop extra fields from styles table
|
request = os.put(style);
|
||||||
t.executeSql('CREATE TABLE newstyles (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, url TEXT, updateUrl TEXT, md5Url TEXT, name TEXT NOT NULL, enabled INTEGER NOT NULL);');
|
request.onsuccess = function(event) {
|
||||||
t.executeSql('INSERT INTO newstyles (id, url, updateUrl, md5Url, name, enabled) SELECT id, url, updateUrl, md5Url, name, enabled FROM styles;');
|
notifyAllTabs({method: "styleUpdated", style: style});
|
||||||
t.executeSql('DROP TABLE styles;');
|
if (callback) {
|
||||||
t.executeSql('ALTER TABLE newstyles RENAME TO styles;');
|
callback(style);
|
||||||
}, error, function() {dbV13(d, error, done)});
|
}
|
||||||
}
|
};
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
function dbV13(d, error, done) {
|
// Create
|
||||||
d.changeVersion(d.version, '1.3', function (t) {
|
// Set optional things to null if they're undefined
|
||||||
// clear out orphans
|
["updateUrl", "md5Url", "url", "originalMd5"].filter(function(att) {
|
||||||
t.executeSql('DELETE FROM section_meta WHERE section_id IN (SELECT sections.id FROM sections LEFT JOIN styles ON styles.id = sections.style_id WHERE styles.id IS NULL);');
|
return !(att in o);
|
||||||
t.executeSql('DELETE FROM sections WHERE id IN (SELECT sections.id FROM sections LEFT JOIN styles ON styles.id = sections.style_id WHERE styles.id IS NULL);');
|
}).forEach(function(att) {
|
||||||
}, error, function() { dbV14(d, error, done)});
|
o[att] = null;
|
||||||
}
|
});
|
||||||
|
// Set to enabled if not set
|
||||||
function dbV14(d, error, done) {
|
if (!("enabled" in o)) {
|
||||||
d.changeVersion(d.version, '1.4', function (t) {
|
o.enabled = true;
|
||||||
t.executeSql('UPDATE styles SET url = null WHERE url = "undefined";');
|
}
|
||||||
}, error, function() { dbV15(d, error, done)});
|
// Make sure it's not null - that makes indexeddb sad
|
||||||
}
|
delete o["id"];
|
||||||
|
var request = os.add(o);
|
||||||
function dbV15(d, error, done) {
|
request.onsuccess = function(event) {
|
||||||
d.changeVersion(d.version, '1.5', function (t) {
|
// Give it the ID that was generated
|
||||||
t.executeSql('ALTER TABLE styles ADD COLUMN originalMd5 TEXT NULL;');
|
o.id = event.target.result;
|
||||||
}, error, function() { done(d); });
|
notifyAllTabs({method: "styleAdded", style: o});
|
||||||
|
if (callback) {
|
||||||
|
callback(o);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function enableStyle(id, enabled) {
|
function enableStyle(id, enabled) {
|
||||||
getDatabase(function(db) {
|
saveStyle({id: id, enabled: enabled}, function(style) {
|
||||||
db.transaction(function (t) {
|
handleUpdate(style);
|
||||||
t.executeSql("UPDATE styles SET enabled = ? WHERE id = ?;", [enabled, id]);
|
notifyAllTabs({method: "styleUpdated", style: style});
|
||||||
}, reportError, function() {
|
|
||||||
chrome.runtime.sendMessage({method: "styleChanged"});
|
|
||||||
chrome.runtime.sendMessage({method: "getStyles", id: id}, function(styles) {
|
|
||||||
handleUpdate(styles[0]);
|
|
||||||
notifyAllTabs({method: "styleUpdated", style: styles[0]});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteStyle(id) {
|
function deleteStyle(id) {
|
||||||
getDatabase(function(db) {
|
getDatabase(function(db) {
|
||||||
db.transaction(function (t) {
|
var tx = db.transaction(["styles"], "readwrite");
|
||||||
t.executeSql('DELETE FROM section_meta WHERE section_id IN (SELECT id FROM sections WHERE style_id = ?);', [id]);
|
var os = tx.objectStore("styles");
|
||||||
t.executeSql('DELETE FROM sections WHERE style_id = ?;', [id]);
|
var request = os.delete(Number(id));
|
||||||
t.executeSql("DELETE FROM styles WHERE id = ?;", [id]);
|
request.onsuccess = function(event) {
|
||||||
}, reportError, function() {
|
|
||||||
chrome.runtime.sendMessage({method: "styleChanged"});
|
|
||||||
handleDelete(id);
|
handleDelete(id);
|
||||||
notifyAllTabs({method: "styleDeleted", id: id});
|
notifyAllTabs({method: "styleDeleted", id: id});
|
||||||
});
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,6 +151,13 @@ function reportError() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fixBoolean(b) {
|
||||||
|
if (typeof b != "undefined") {
|
||||||
|
return b != "false";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
function getDomains(url) {
|
function getDomains(url) {
|
||||||
if (url.indexOf("file:") == 0) {
|
if (url.indexOf("file:") == 0) {
|
||||||
return [];
|
return [];
|
||||||
|
@ -132,10 +181,80 @@ function getType(o) {
|
||||||
throw "Not supported - " + o;
|
throw "Not supported - " + o;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var namespacePattern = /^\s*(@namespace[^;]+;\s*)+$/;
|
||||||
|
function getApplicableSections(style, url) {
|
||||||
|
var sections = style.sections.filter(function(section) {
|
||||||
|
return sectionAppliesToUrl(section, url);
|
||||||
|
});
|
||||||
|
// ignore if it's just namespaces
|
||||||
|
if (sections.length == 1 && namespacePattern.test(sections[0].code)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return sections;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sectionAppliesToUrl(section, url) {
|
||||||
|
// only http, https, file, and chrome-extension allowed
|
||||||
|
if (url.indexOf("http") != 0 && url.indexOf("file") != 0 && url.indexOf("chrome-extension") != 0 && url.indexOf("ftp") != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// other extensions can't be styled
|
||||||
|
if (url.indexOf("chrome-extension") == 0 && url.indexOf(chrome.extension.getURL("")) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (section.urls.length == 0 && section.domains.length == 0 && section.urlPrefixes.length == 0 && section.regexps.length == 0) {
|
||||||
|
//console.log(section.id + " is global");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (section.urls.indexOf(url) != -1) {
|
||||||
|
//console.log(section.id + " applies to " + url + " due to URL rules");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (section.urlPrefixes.some(function(prefix) {
|
||||||
|
return url.indexOf(prefix) == 0;
|
||||||
|
})) {
|
||||||
|
//console.log(section.id + " applies to " + url + " due to URL prefix rules");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (section.domains.length > 0 && getDomains(url).some(function(domain) {
|
||||||
|
return section.domains.indexOf(domain) != -1;
|
||||||
|
})) {
|
||||||
|
//console.log(section.id + " applies due to " + url + " due to domain rules");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (section.regexps.some(function(regexp) {
|
||||||
|
// we want to match the full url, so add ^ and $ if not already present
|
||||||
|
if (regexp[0] != "^") {
|
||||||
|
regexp = "^" + regexp;
|
||||||
|
}
|
||||||
|
if (regexp[regexp.length - 1] != "$") {
|
||||||
|
regexp += "$";
|
||||||
|
}
|
||||||
|
var re = runTryCatch(function() { return new RegExp(regexp) });
|
||||||
|
if (re) {
|
||||||
|
return (re).test(url);
|
||||||
|
} else {
|
||||||
|
console.log(section.id + "'s regexp '" + regexp + "' is not valid");
|
||||||
|
}
|
||||||
|
})) {
|
||||||
|
//console.log(section.id + " applies to " + url + " due to regexp rules");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//console.log(section.id + " does not apply due to " + url);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
function isCheckbox(el) {
|
function isCheckbox(el) {
|
||||||
return el.nodeName.toLowerCase() == "input" && "checkbox" == el.type.toLowerCase();
|
return el.nodeName.toLowerCase() == "input" && "checkbox" == el.type.toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {}
|
||||||
|
}
|
||||||
|
|
||||||
// Accepts an array of pref names (values are fetched via prefs.get)
|
// Accepts an array of pref names (values are fetched via prefs.get)
|
||||||
// and establishes a two-way connection between the document elements and the actual prefs
|
// and establishes a two-way connection between the document elements and the actual prefs
|
||||||
function setupLivePrefs(IDs) {
|
function setupLivePrefs(IDs) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user