diff --git a/background.js b/background.js index 86da312d..a0a7e8e6 100644 --- a/background.js +++ b/background.js @@ -89,7 +89,7 @@ function getStyles(options, callback) { } 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, sections: []}; + 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 (currentSection == null || currentSection.id != values.section_id) { @@ -199,15 +199,18 @@ function saveStyle(o, callback) { 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"].filter(function(att) { + ["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) VALUES (?, ?, ?, ?, ?);', [o.name, true, o.url, o.updateUrl, o.md5Url]); + 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) { diff --git a/install.js b/install.js index e82460c0..7ae67499 100644 --- a/install.js +++ b/install.js @@ -4,25 +4,37 @@ chrome.extension.sendMessage({method: "getStyles", url: getMeta("stylish-id-url" } else { var installedStyle = response[0]; // maybe an update is needed - getResource(getMeta("stylish-code-chrome"), function(code) { - // this would indicate a failure (a style with settings?). - if (code == null) { - sendEvent("styleCanBeUpdatedChrome"); - } - var json = JSON.parse(code); - if (json.sections.length == installedStyle.sections.length) { - if (json.sections.every(function(section) { - return installedStyle.sections.some(function(installedSection) { - return sectionsAreEqual(section, installedSection); - }); - })) { - // everything's the same - sendEvent("styleAlreadyInstalledChrome"); - return; - }; - } - sendEvent("styleCanBeUpdatedChrome"); - }); + // use the md5 if available + var md5Url = getMeta("stylish-md5-url"); + if (md5Url && installedStyle.md5Url && installedStyle.originalMd5) { + getResource(md5Url, function(md5) { + if (md5 == installedStyle.originalMd5) { + sendEvent("styleAlreadyInstalledChrome", {updateUrl: installedStyle.updateUrl}); + } else { + sendEvent("styleCanBeUpdatedChrome", {updateUrl: installedStyle.updateUrl}); + } + }); + } else { + getResource(getMeta("stylish-code-chrome"), function(code) { + // this would indicate a failure (a style with settings?). + if (code == null) { + sendEvent("styleCanBeUpdatedChrome", {updateUrl: installedStyle.updateUrl}); + } + var json = JSON.parse(code); + if (json.sections.length == installedStyle.sections.length) { + if (json.sections.every(function(section) { + return installedStyle.sections.some(function(installedSection) { + return sectionsAreEqual(section, installedSection); + }); + })) { + // everything's the same + sendEvent("styleAlreadyInstalledChrome", {updateUrl: installedStyle.updateUrl}); + return; + }; + } + sendEvent("styleCanBeUpdatedChrome", {updateUrl: installedStyle.updateUrl}); + }); + } } }); @@ -49,9 +61,11 @@ function arraysAreEqual(a, b) { }); } -function sendEvent(type) { - var stylishEvent = document.createEvent("Events"); - stylishEvent.initEvent(type, false, false, document.defaultView, null); +function sendEvent(type, data) { + if (typeof data == "undefined") { + data = null; + } + var stylishEvent = new CustomEvent(type, {detail: data}); document.dispatchEvent(stylishEvent); } @@ -77,7 +91,9 @@ document.addEventListener("stylishUpdateChrome", function() { if (confirm(chrome.i18n.getMessage('styleUpdate', [style.name]))) { getResource(getMeta("stylish-code-chrome"), function(code) { var json = JSON.parse(code); - chrome.extension.sendMessage({method: "saveStyle", id: style.id, sections: json.sections}, function() { + json.method = "saveStyle"; + json.id = style.id; + chrome.extension.sendMessage(json, function() { sendEvent("styleInstalledChrome"); }); }); diff --git a/manage.js b/manage.js index 3d7c2d08..ccdd9e3e 100644 --- a/manage.js +++ b/manage.js @@ -1,6 +1,8 @@ var styleTemplate = document.createElement("div"); styleTemplate.innerHTML = "

"; +var lastUpdatedStyleId = null; + var appliesToExtraTemplate = document.createElement("span"); appliesToExtraTemplate.className = "applies-to-extra"; appliesToExtraTemplate.innerHTML = " " + t('appliesDisplayTruncatedSuffix'); @@ -21,6 +23,12 @@ function createStyleElement(style) { if (style.updateUrl) { e.setAttribute("style-update-url", style.updateUrl); } + if (style.md5Url) { + e.setAttribute("style-md5-url", style.md5Url); + } + if (style.originalMd5) { + e.setAttribute("style-original-md5", style.originalMd5); + } var styleName = e.querySelector(".style-name"); styleName.appendChild(document.createTextNode(style.name)); @@ -131,7 +139,13 @@ chrome.extension.onMessage.addListener(function(request, sender, sendResponse) { function handleUpdate(style) { var installed = document.getElementById("installed"); - installed.replaceChild(createStyleElement(style), installed.querySelector("[style-id='" + style.id + "']")); + var element = createStyleElement(style); + installed.replaceChild(element, installed.querySelector("[style-id='" + style.id + "']")); + if (style.id == lastUpdatedStyleId) { + lastUpdatedStyleId = null; + element.className = element.className += " update-done"; + element.querySelector(".update-note").innerHTML = t('updateCompleted'); + }; } function handleDelete(id) { @@ -152,31 +166,78 @@ function checkUpdate(element) { element.className = element.className.replace("checking-update", "").replace("no-update", "").replace("can-update", "") + " checking-update"; var id = element.getAttribute("style-id"); var url = element.getAttribute("style-update-url"); + var md5Url = element.getAttribute("style-md5-url"); + var originalMd5 = element.getAttribute("style-original-md5"); + + function handleSuccess(forceUpdate, serverJson) { + chrome.extension.sendMessage({method: "getStyles", id: id}, function(styles) { + var style = styles[0]; + if (!forceUpdate && codeIsEqual(style.sections, serverJson.sections)) { + handleNeedsUpdate("no", id, serverJson); + } else { + handleNeedsUpdate("yes", id, serverJson); + } + }); + } + + function handleFailure(status) { + if (status == 0) { + handleNeedsUpdate(t('updateCheckFailServerUnreachable'), id, null); + } else { + handleNeedsUpdate(t('updateCheckFailBadResponseCode', [status]), id, null); + } + } + + if (!md5Url || !originalMd5) { + checkUpdateFullCode(url, false, handleSuccess, handleFailure) + } else { + checkUpdateMd5(originalMd5, md5Url, function(needsUpdate) { + if (needsUpdate) { + // If the md5 shows a change we will update regardless of whether the code looks different + checkUpdateFullCode(url, true, handleSuccess, handleFailure); + } else { + handleNeedsUpdate("no", id, null); + } + }, handleFailure); + } +} + +function checkUpdateFullCode(url, forceUpdate, successCallback, failureCallback) { + download(url, function(responseText) { + successCallback(forceUpdate, JSON.parse(responseText)); + }, failureCallback); +} + +function checkUpdateMd5(originalMd5, md5Url, successCallback, failureCallback) { + download(md5Url, function(responseText) { + if (responseText.length != 32) { + failureCallback(-1); + return; + } + successCallback(responseText != originalMd5); + }, failureCallback); +} + +function download(url, successCallback, failureCallback) { var xhr = new XMLHttpRequest(); - xhr.open("GET", url, true); xhr.onreadystatechange = function (aEvt) { if (xhr.readyState == 4) { if (xhr.status == 200) { - checkNeedsUpdate(id, JSON.parse(xhr.responseText)); - } else if (xhr.status == 0) { - handleNeedsUpdate(t('updateCheckFailServerUnreachable'), id, null); + successCallback(xhr.responseText) } else { - handleNeedsUpdate(t('updateCheckFailBadResponseCode', [xhr.status]), id, null); + failureCallback(xhr.status); } } - }; - xhr.send(null); -} - -function checkNeedsUpdate(id, serverJson) { - chrome.extension.sendMessage({method: "getStyles", id: id}, function(styles) { - var style = styles[0]; - if (codeIsEqual(style.sections, serverJson.sections)) { - handleNeedsUpdate("no", id, serverJson); - } else { - handleNeedsUpdate("yes", id, serverJson); - } - }); + } + if (url.length > 2000) { + var parts = url.split("?"); + xhr.open("POST", parts[0], true); + xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded"); + xhr.send(parts[1]); + } else { + xhr.open("GET", url, true); + xhr.send(); + } } function handleNeedsUpdate(needsUpdate, id, serverJson) { @@ -200,11 +261,16 @@ function handleNeedsUpdate(needsUpdate, id, serverJson) { function doUpdate(event) { var element = getStyleElement(event); - chrome.extension.sendMessage({method: "saveStyle", id: element.getAttribute('style-id'), sections: element.updatedCode.sections}, function() { - element.updatedCode = ""; - element.className = element.className.replace("can-update", "update-done"); - element.querySelector(".update-note").innerHTML = t('updateCompleted'); - }); + + var updatedCode = element.updatedCode; + // update everything but name + delete updatedCode.name; + updatedCode.id = element.getAttribute('style-id'); + updatedCode.method = "saveStyle"; + + // updating the UI will be handled by the general update listener + lastUpdatedStyleId = updatedCode.id; + chrome.extension.sendMessage(updatedCode); } function codeIsEqual(a, b) { diff --git a/storage.js b/storage.js index 3bee18b3..f729ad45 100644 --- a/storage.js +++ b/storage.js @@ -18,6 +18,8 @@ function getDatabase(ready, error) { 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); } @@ -64,6 +66,12 @@ function dbV14(d, error, done) { }, error, function() { done(d)}); } +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)}); +} + function enableStyle(id, enabled) { getDatabase(function(db) { db.transaction(function (t) {