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);
|
||||
break;
|
||||
case "styleUpdated":
|
||||
if (request.style.enabled == "true") {
|
||||
if (request.style.enabled) {
|
||||
retireStyle(request.style.id);
|
||||
// fallthrough to "styleAdded"
|
||||
} else {
|
||||
|
@ -37,7 +37,7 @@ chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
|
|||
break;
|
||||
}
|
||||
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);
|
||||
}
|
||||
break;
|
||||
|
|
283
background.js
283
background.js
|
@ -66,9 +66,6 @@ chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
|
|||
case "saveStyle":
|
||||
saveStyle(request, sendResponse);
|
||||
return true;
|
||||
case "styleChanged":
|
||||
cachedStyles = null;
|
||||
break;
|
||||
case "healthCheck":
|
||||
getDatabase(function() { sendResponse(true); }, function() { sendResponse(false); });
|
||||
break;
|
||||
|
@ -126,279 +123,6 @@ function disableAllStylesToggle(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.
|
||||
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;
|
||||
getCodeMirrorThemes(function(themes) {
|
||||
codeMirrorThemes = themes;
|
||||
|
|
2
edit.js
2
edit.js
|
@ -1093,7 +1093,7 @@ function init() {
|
|||
|
||||
function initWithStyle(style) {
|
||||
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;
|
||||
// if this was done in response to an update, we need to clear existing sections
|
||||
getSections().forEach(function(div) { div.remove(); });
|
||||
|
|
|
@ -29,7 +29,7 @@ function showStyles(styles) {
|
|||
|
||||
function createStyleElement(style) {
|
||||
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);
|
||||
if (style.updateUrl) {
|
||||
e.setAttribute("style-update-url", style.updateUrl);
|
||||
|
@ -190,7 +190,10 @@ function handleUpdate(style) {
|
|||
}
|
||||
|
||||
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) {
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
"https://userstyles.org/"
|
||||
],
|
||||
"background": {
|
||||
"scripts": ["messaging.js", "storage.js", "background.js"]
|
||||
"scripts": ["messaging.js", "storage-websql.js", "storage.js", "background.js"]
|
||||
},
|
||||
"commands": {
|
||||
"openManage": {
|
||||
|
|
4
popup.js
4
popup.js
|
@ -88,9 +88,9 @@ function createStyleElement(style) {
|
|||
var e = template.style.cloneNode(true);
|
||||
var checkbox = e.querySelector(".checker");
|
||||
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);
|
||||
var styleName = e.querySelector(".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); });
|
||||
}
|
||||
}
|
281
storage.js
281
storage.js
|
@ -1,102 +1,144 @@
|
|||
var stylishDb = null;
|
||||
function getDatabase(ready, error) {
|
||||
if (stylishDb != null && stylishDb.version == "1.5") {
|
||||
ready(stylishDb);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
stylishDb = openDatabase('stylish', '', 'Stylish Styles', 5*1024*1024);
|
||||
} catch (ex) {
|
||||
error();
|
||||
throw ex;
|
||||
}
|
||||
if (stylishDb.version == "1.0" || stylishDb.version == "") {
|
||||
dbV11(stylishDb, error, ready);
|
||||
} else if (stylishDb.version == "1.1") {
|
||||
dbV12(stylishDb, error, ready);
|
||||
} else if (stylishDb.version == "1.2") {
|
||||
dbV13(stylishDb, error, ready);
|
||||
} else if (stylishDb.version == "1.3") {
|
||||
dbV14(stylishDb, error, ready);
|
||||
} else if (stylishDb.version == "1.4") {
|
||||
dbV15(stylishDb, error, ready);
|
||||
} else {
|
||||
ready(stylishDb);
|
||||
var dbOpenRequest = window.indexedDB.open("stylish", 2);
|
||||
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 dbV11(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() {dbV12(d, error, done)});
|
||||
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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
});
|
||||
return styles;
|
||||
}
|
||||
|
||||
function dbV12(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() {dbV13(d, error, done)});
|
||||
}
|
||||
function saveStyle(o, callback) {
|
||||
getDatabase(function(db) {
|
||||
var tx = db.transaction(["styles"], "readwrite");
|
||||
var os = tx.objectStore("styles");
|
||||
|
||||
function dbV13(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() { dbV14(d, error, done)});
|
||||
}
|
||||
// Update
|
||||
if (o.id) {
|
||||
var request = os.get(Number(o.id));
|
||||
request.onsuccess = function(event) {
|
||||
var style = request.result;
|
||||
for (var prop in o) {
|
||||
if (prop == "id") {
|
||||
continue;
|
||||
}
|
||||
style[prop] = o[prop];
|
||||
}
|
||||
request = os.put(style);
|
||||
request.onsuccess = function(event) {
|
||||
notifyAllTabs({method: "styleUpdated", style: style});
|
||||
if (callback) {
|
||||
callback(style);
|
||||
}
|
||||
};
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
function dbV14(d, error, done) {
|
||||
d.changeVersion(d.version, '1.4', function (t) {
|
||||
t.executeSql('UPDATE styles SET url = null WHERE url = "undefined";');
|
||||
}, error, function() { dbV15(d, error, done)});
|
||||
}
|
||||
|
||||
function dbV15(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); });
|
||||
// Create
|
||||
// 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;
|
||||
});
|
||||
// Set to enabled if not set
|
||||
if (!("enabled" in o)) {
|
||||
o.enabled = true;
|
||||
}
|
||||
// Make sure it's not null - that makes indexeddb sad
|
||||
delete o["id"];
|
||||
var request = os.add(o);
|
||||
request.onsuccess = function(event) {
|
||||
// Give it the ID that was generated
|
||||
o.id = event.target.result;
|
||||
notifyAllTabs({method: "styleAdded", style: o});
|
||||
if (callback) {
|
||||
callback(o);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function enableStyle(id, enabled) {
|
||||
getDatabase(function(db) {
|
||||
db.transaction(function (t) {
|
||||
t.executeSql("UPDATE styles SET enabled = ? WHERE id = ?;", [enabled, id]);
|
||||
}, 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]});
|
||||
});
|
||||
});
|
||||
saveStyle({id: id, enabled: enabled}, function(style) {
|
||||
handleUpdate(style);
|
||||
notifyAllTabs({method: "styleUpdated", style: style});
|
||||
});
|
||||
}
|
||||
|
||||
function deleteStyle(id) {
|
||||
getDatabase(function(db) {
|
||||
db.transaction(function (t) {
|
||||
t.executeSql('DELETE FROM section_meta WHERE section_id IN (SELECT id FROM sections WHERE style_id = ?);', [id]);
|
||||
t.executeSql('DELETE FROM sections WHERE style_id = ?;', [id]);
|
||||
t.executeSql("DELETE FROM styles WHERE id = ?;", [id]);
|
||||
}, reportError, function() {
|
||||
chrome.runtime.sendMessage({method: "styleChanged"});
|
||||
var tx = db.transaction(["styles"], "readwrite");
|
||||
var os = tx.objectStore("styles");
|
||||
var request = os.delete(Number(id));
|
||||
request.onsuccess = function(event) {
|
||||
handleDelete(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) {
|
||||
if (url.indexOf("file:") == 0) {
|
||||
return [];
|
||||
|
@ -132,10 +181,80 @@ function getType(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) {
|
||||
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)
|
||||
// and establishes a two-way connection between the document elements and the actual prefs
|
||||
function setupLivePrefs(IDs) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user