forked from greasemonkey/greasemonkey
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refs greasemonkey#2531
- Loading branch information
Showing
10 changed files
with
274 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
// Private implementation. | ||
(function() { | ||
|
||
const CHANGE_RATE = 1.25; | ||
const MAX_UPDATE_IN_MS = (1000 * 60 * 60 * 24 * 7); | ||
const MIN_UPDATE_IN_MS = (1000 * 60 * 60 * 3); | ||
|
||
|
||
let gTimer = null; | ||
|
||
|
||
function checkForUpdate(uuid) { | ||
return new Promise((resolve, reject) => { | ||
let userScript = UserScriptRegistry.scriptByUuid(uuid); | ||
if (!userScript) { | ||
// Uninstalled since the update check was queued. | ||
reject('ignore'); | ||
return; | ||
} | ||
|
||
let windowKey = 'updateWindow.' + uuid; | ||
chrome.storage.local.get(windowKey, windowVal => { | ||
let abort = false; | ||
let downloader = new UserScriptDownloader(); | ||
downloader.setScriptUrl(userScript.downloadUrl); | ||
downloader.start(details => { | ||
// Return false here will stop the downloader -- skipping e.g. | ||
// @require, @icon, etc. downloads. | ||
// `compareVersions()` returns -1 when its second argument is "larger". | ||
let comparison = compareVersions(userScript.version, details.version); | ||
// So we should abort if we don't get -1. | ||
abort = comparison !== -1; | ||
// And we should return "not stop". | ||
return !abort; | ||
}).then(async () => { | ||
let window = fuzz(windowVal[windowKey] || MAX_UPDATE_IN_MS); | ||
if (abort) { | ||
window *= CHANGE_RATE; | ||
} else { | ||
window /= CHANGE_RATE; | ||
await downloader.installFromBackground(); | ||
} | ||
window = Math.min(window, MAX_UPDATE_IN_MS); | ||
window = Math.max(window, MIN_UPDATE_IN_MS); | ||
|
||
let d = {}; | ||
d[windowKey] = window; | ||
d['updateNextAt.' + userScript.uuid] = new Date().getTime() + window; | ||
chrome.storage.local.set(d, logUnhandledError); | ||
|
||
if (abort) { | ||
resolve('no new version'); | ||
} else { | ||
resolve('updated'); | ||
} | ||
}).catch((e) => { | ||
reject(e); | ||
}); | ||
}); | ||
}); | ||
} | ||
|
||
|
||
function fuzz(num) { | ||
return num * (Math.random() * 0.1 + 0.95); | ||
} | ||
|
||
|
||
/** Visible only for testing! */ | ||
window._pickNextScriptAutoUpdate = async function() { | ||
return new Promise((resolve, reject) => { | ||
let nextTime = null; | ||
let nextUuid = null; | ||
|
||
let updateNextAtKeys = []; | ||
let userScriptIterator = UserScriptRegistry.scriptsToRunAt(); | ||
for (let userScript of userScriptIterator) { | ||
if (!userScript.downloadUrl) continue; | ||
updateNextAtKeys.push('updateNextAt.' + userScript.uuid); | ||
} | ||
if (updateNextAtKeys.length == 0) { | ||
reject(new Error('no scripts to update')); | ||
return; | ||
} | ||
|
||
let defaultCheckTime = new Date().getTime() + MIN_UPDATE_IN_MS; | ||
chrome.storage.local.get(updateNextAtKeys, vs => { | ||
for (let k of updateNextAtKeys) { | ||
let uuid = k.replace('updateNextAt.', ''); | ||
let v = vs[k]; | ||
|
||
if (!nextTime || !nextUuid || v < nextTime) { | ||
nextTime = v || defaultCheckTime; | ||
nextUuid = uuid; | ||
} | ||
} | ||
|
||
if (nextUuid) { | ||
resolve([nextUuid, nextTime]); | ||
} else { | ||
reject('Could not find next script to update.'); | ||
} | ||
}); | ||
}); | ||
}; | ||
|
||
window.scheduleNextScriptAutoUpdate = async function() { | ||
let nextUuid, nextTime; | ||
try { | ||
[nextUuid, nextTime] = await _pickNextScriptAutoUpdate(); | ||
} catch (e) { | ||
setTimeout(scheduleNextScriptAutoUpdate, fuzz(1000 * 60 * 15)); | ||
return; | ||
} | ||
|
||
let delay = nextTime - new Date().getTime(); | ||
gTimer = setTimeout(async (nextUuid) => { | ||
await checkForUpdate(nextUuid); | ||
await scheduleNextScriptAutoUpdate(); | ||
}, delay, nextUuid); | ||
}; | ||
|
||
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
// scheduleNextScriptAutoUpdate(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
'use strict'; | ||
describe('bg/updater', () => { | ||
before(() => sinon.stub(UserScriptRegistry, 'scriptsToRunAt')); | ||
after(() => UserScriptRegistry.scriptsToRunAt.restore()); | ||
|
||
describe('pickNextScriptAutoUpdate', () => { | ||
it('throws with no scripts', () => { | ||
UserScriptRegistry.scriptsToRunAt.returns([{}]); | ||
_pickNextScriptAutoUpdate() | ||
.catch(e => assert.equal(e.message, 'no scripts to update')); | ||
}); | ||
|
||
it('throws with only one local script', () => { | ||
UserScriptRegistry.scriptsToRunAt.returns([{ | ||
// no downloadUrl here! | ||
'uuid': '6e23d732-9a17-4a23-9c3a-5203d76065d7' | ||
}]); | ||
_pickNextScriptAutoUpdate() | ||
.catch(e => assert.equal(e.message, 'no scripts to update')); | ||
}); | ||
|
||
it('returns a single script', async () => { | ||
UserScriptRegistry.scriptsToRunAt.returns([{ | ||
'downloadUrl': 'http://example.com/anything.user.js', | ||
'uuid': '0b6c8047-8c9b-4bbe-bf82-f4e376e284a0', | ||
}]); | ||
chrome.storage.local.get.callsArgWith(1, {}); | ||
let [nextUuid, _] = await _pickNextScriptAutoUpdate(); | ||
assert.equal(nextUuid, '0b6c8047-8c9b-4bbe-bf82-f4e376e284a0'); | ||
}); | ||
|
||
it('returns the proper script from among two', async () => { | ||
UserScriptRegistry.scriptsToRunAt.returns([{ | ||
'downloadUrl': 'http://example.com/anything1.user.js', | ||
'uuid': '7289270f-c30d-41e5-932c-560d81315565', | ||
},{ | ||
'downloadUrl': 'http://example.com/anything2.user.js', | ||
'uuid': '46a3926d-f64e-4800-a915-4afbe44da4fc', | ||
}]); | ||
chrome.storage.local.get.callsArgWith(1, { | ||
'updateNextAt.7289270f-c30d-41e5-932c-560d81315565': 1, | ||
'updateNextAt.46a3926d-f64e-4800-a915-4afbe44da4fc': 2, | ||
}); | ||
let [nextUuid, _] = await _pickNextScriptAutoUpdate(); | ||
assert.equal(nextUuid, '7289270f-c30d-41e5-932c-560d81315565'); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
Extracted from distribution version 3.3.0: | ||
|
||
https://github.com/omichelsen/compare-versions/releases/tag/v3.3.0 | ||
|
||
Reused under the terms of the MIT license. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
/* global define */ | ||
(function (root, factory) { | ||
/* istanbul ignore next */ | ||
if (typeof define === 'function' && define.amd) { | ||
define([], factory); | ||
} else if (typeof exports === 'object') { | ||
module.exports = factory(); | ||
} else { | ||
root.compareVersions = factory(); | ||
} | ||
}(this, function () { | ||
|
||
var semver = /^v?(?:\d+)(\.(?:[x*]|\d+)(\.(?:[x*]|\d+)(\.(?:[x*]|\d+))?(?:-[\da-z\-]+(?:\.[\da-z\-]+)*)?(?:\+[\da-z\-]+(?:\.[\da-z\-]+)*)?)?)?$/i; | ||
|
||
function indexOrEnd(str, q) { | ||
return str.indexOf(q) === -1 ? str.length : str.indexOf(q); | ||
} | ||
|
||
function split(v) { | ||
var c = v.replace(/^v/, '').replace(/\+.*$/, ''); | ||
var patchIndex = indexOrEnd(c, '-'); | ||
var arr = c.substring(0, patchIndex).split('.'); | ||
arr.push(c.substring(patchIndex + 1)); | ||
return arr; | ||
} | ||
|
||
function tryParse(v) { | ||
return isNaN(Number(v)) ? v : Number(v); | ||
} | ||
|
||
function validate(version) { | ||
if (typeof version !== 'string') { | ||
throw new TypeError('Invalid argument expected string'); | ||
} | ||
if (!semver.test(version)) { | ||
throw new Error('Invalid argument not valid semver'); | ||
} | ||
} | ||
|
||
return function compareVersions(v1, v2) { | ||
[v1, v2].forEach(validate); | ||
|
||
var s1 = split(v1); | ||
var s2 = split(v2); | ||
|
||
for (var i = 0; i < Math.max(s1.length - 1, s2.length - 1); i++) { | ||
var n1 = parseInt(s1[i] || 0, 10); | ||
var n2 = parseInt(s2[i] || 0, 10); | ||
|
||
if (n1 > n2) return 1; | ||
if (n2 > n1) return -1; | ||
} | ||
|
||
var sp1 = s1[s1.length - 1]; | ||
var sp2 = s2[s2.length - 1]; | ||
|
||
if (sp1 && sp2) { | ||
var p1 = sp1.split('.').map(tryParse); | ||
var p2 = sp2.split('.').map(tryParse); | ||
|
||
for (i = 0; i < Math.max(p1.length, p2.length); i++) { | ||
if (p1[i] === undefined || typeof p2[i] === 'string' && typeof p1[i] === 'number') return -1; | ||
if (p2[i] === undefined || typeof p1[i] === 'string' && typeof p2[i] === 'number') return 1; | ||
|
||
if (p1[i] > p2[i]) return 1; | ||
if (p2[i] > p1[i]) return -1; | ||
} | ||
} else if (sp1 || sp2) { | ||
return sp1 ? -1 : 1; | ||
} | ||
|
||
return 0; | ||
}; | ||
|
||
})); |