author | Ehsan Akhgari <ehsan@mozilla.com> |
Sat, 19 Jan 2013 11:10:03 -0500 | |
changeset 119364 | 323d01be9d0d1e3650bdfdbd688932d749316cb8 |
parent 119363 | 8472e5898021a48e06e3bb3656e269ad22cc2b05 (current diff) |
parent 119131 | 01a8559f55605a5458de7d42544af2f0c6b6a462 (diff) |
child 119365 | c059c73cd16c2c335bc9a6490a328a55be28eb23 |
push id | 24197 |
push user | ryanvm@gmail.com |
push date | Sun, 20 Jan 2013 05:25:28 +0000 |
treeherder | mozilla-central@1d122eaa9070 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 21.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/browser/app/blocklist.xml +++ b/browser/app/blocklist.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> -<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1357926611000"> +<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1358464191000"> <emItems> <emItem blockID="i58" id="webmaster@buzzzzvideos.info"> <versionRange minVersion="0" maxVersion="*"> </versionRange> </emItem> <emItem blockID="i41" id="{99079a25-328f-4bd4-be04-00955acaa0a7}"> <versionRange minVersion="0.1" maxVersion="4.3.1.00" severity="1"> </versionRange> @@ -215,16 +215,20 @@ </versionRange> </emItem> <emItem blockID="i22" id="ShopperReports@ShopperReports.com"> <versionRange minVersion="3.1.22.0" maxVersion="3.1.22.0"> </versionRange> </emItem> <emItem blockID="i44" id="sigma@labs.mozilla"> </emItem> + <emItem blockID="i246" id="support@vide1flash2.com"> + <versionRange minVersion="0" maxVersion="*" severity="3"> + </versionRange> + </emItem> <emItem blockID="i48" id="admin@youtubespeedup.com"> </emItem> <emItem blockID="i109" id="{392e123b-b691-4a5e-b52f-c4c1027e749c}"> <versionRange minVersion="0" maxVersion="*"> </versionRange> </emItem> <emItem blockID="i79" id="GifBlock@facebook.com"> <versionRange minVersion="0" maxVersion="*"> @@ -583,31 +587,31 @@ <pluginItem blockID="p178"> <match name="filename" exp="(NPSWF[0-9_]*\.dll)|(Flash\ Player\.plugin)" /> <versionRange minVersion="11.0" maxVersion="11.4.402.286.999" severity="0" vulnerabilitystatus="1"> <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"> <versionRange minVersion="19.0a1" maxVersion="*" /> </targetApplication> </versionRange> </pluginItem> <pluginItem blockID="p180"> - <match name="filename" exp="JavaAppletPlugin\.plugin" /> <versionRange minVersion="Java 7 Update 0" maxVersion="Java 7 Update 10" severity="0" vulnerabilitystatus="2"> + <match name="filename" exp="JavaAppletPlugin\.plugin" /> <versionRange minVersion="Java 7 Update 0" maxVersion="Java 7 Update 11" severity="0" vulnerabilitystatus="2"> <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"> <versionRange minVersion="17.0" maxVersion="*" /> </targetApplication> </versionRange> </pluginItem> <pluginItem blockID="p182"> - <match name="name" exp="Java\(TM\) Platform SE 7 U([0-9]|10)(\s[^\d\._U]|$)" /> <match name="filename" exp="npjp2\.dll" /> <versionRange severity="0" vulnerabilitystatus="2"> + <match name="name" exp="Java\(TM\) Platform SE 7 U([0-9]|(1[0-1]))(\s[^\d\._U]|$)" /> <match name="filename" exp="npjp2\.dll" /> <versionRange severity="0" vulnerabilitystatus="2"> <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"> <versionRange minVersion="17.0" maxVersion="*" /> </targetApplication> </versionRange> </pluginItem> <pluginItem blockID="p184"> - <match name="name" exp="Java\(TM\) Plug-in 1\.7\.0(_0?([0-9]|10)?)?([^\d\._]|$)" /> <match name="filename" exp="libnpjp2\.so" /> <versionRange severity="0" vulnerabilitystatus="2"> + <match name="name" exp="Java\(TM\) Plug-in 1\.7\.0(_0?([0-9]|(1[0-1]))?)?([^\d\._]|$)" /> <match name="filename" exp="libnpjp2\.so" /> <versionRange severity="0" vulnerabilitystatus="2"> <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"> <versionRange minVersion="17.0" maxVersion="*" /> </targetApplication> </versionRange> </pluginItem> <pluginItem blockID="p186"> <match name="name" exp="Java\(TM\) Platform SE 6 U3[1-8](\s[^\d\._U]|$)" /> <match name="filename" exp="npjp2\.dll" /> <versionRange severity="0" vulnerabilitystatus="2"> <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"> @@ -655,16 +659,32 @@ </pluginItem> <pluginItem os="Darwin" blockID="p242"> <match name="description" exp="Flip4Mac" /> <versionRange minVersion="0" maxVersion="2.4.3.999" severity="1"> <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"> <versionRange minVersion="18.0a1" maxVersion="*" /> </targetApplication> </versionRange> </pluginItem> + <pluginItem blockID="p248"> + <match name="filename" exp="Scorch\.plugin" /> <versionRange minVersion="0" maxVersion="6.2.0" severity="1"></versionRange> + </pluginItem> + <pluginItem blockID="p250"> + <match name="filename" exp="npFoxitReaderPlugin\.dll" /> <versionRange minVersion="0" maxVersion="2.2.1.530" severity="0" vulnerabilitystatus="2"></versionRange> + </pluginItem> + <pluginItem os="Darwin" blockID="p252"> + <match name="filename" exp="AdobePDFViewerNPAPI\.plugin" /> <versionRange minVersion="11.0.0" maxVersion="11.0.01" severity="1"></versionRange> + </pluginItem> + <pluginItem blockID="p254"> + <match name="filename" exp="PDF Browser Plugin\.plugin" /> <versionRange minVersion="0" maxVersion="2.4.2" severity="1"> + <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"> + <versionRange minVersion="18.0a1" maxVersion="*" /> + </targetApplication> + </versionRange> + </pluginItem> </pluginItems> <gfxItems> <gfxBlacklistEntry blockID="g35"> <os>WINNT 6.1</os> <vendor>0x10de</vendor> <devices> <device>0x0a6c</device> </devices> <feature>DIRECT2D</feature> <featureStatus>BLOCKED_DRIVER_VERSION</featureStatus> <driverVersion>8.17.12.5896</driverVersion> <driverVersionComparator>LESS_THAN_OR_EQUAL</driverVersionComparator> </gfxBlacklistEntry> <gfxBlacklistEntry blockID="g36"> <os>WINNT 6.1</os> <vendor>0x10de</vendor> <devices>
--- a/services/sync/modules/engines/history.js +++ b/services/sync/modules/engines/history.js @@ -321,24 +321,16 @@ HistoryStore.prototype = { PlacesUtils.history.removePage(uri); this._log.trace("Removed page: " + [record.id, page.url, page.title]); }, itemExists: function HistStore_itemExists(id) { return !!this._findURLByGUID(id); }, - urlExists: function HistStore_urlExists(url) { - if (typeof(url) == "string") { - url = Utils.makeURI(url); - } - // Don't call isVisited on a null URL to work around crasher bug 492442. - return url ? PlacesUtils.history.isVisited(url) : false; - }, - createRecord: function createRecord(id, collection) { let foo = this._findURLByGUID(id); let record = new HistoryRec(collection, id); if (foo) { record.histUri = foo.url; record.title = foo.title; record.sortindex = foo.frecency; record.visits = this._getVisits(record.histUri);
--- a/services/sync/tests/unit/test_corrupt_keys.js +++ b/services/sync/tests/unit/test_corrupt_keys.js @@ -6,18 +6,19 @@ Cu.import("resource://services-sync/cons Cu.import("resource://services-sync/engines.js"); Cu.import("resource://services-sync/engines/tabs.js"); Cu.import("resource://services-sync/engines/history.js"); Cu.import("resource://services-sync/record.js"); Cu.import("resource://services-sync/service.js"); Cu.import("resource://services-sync/status.js"); Cu.import("resource://services-sync/util.js"); Cu.import("resource://testing-common/services/sync/utils.js"); +Cu.import("resource://gre/modules/commonjs/promise/core.js"); -add_test(function test_locally_changed_keys() { +add_task(function test_locally_changed_keys() { let passphrase = "abcdeabcdeabcdeabcdeabcdea"; let hmacErrorCount = 0; function counting(f) { return function() { hmacErrorCount++; return f.call(this); }; @@ -140,21 +141,21 @@ add_test(function test_locally_changed_k _("HMAC error count: " + hmacErrorCount); // Now syncing should succeed, after one HMAC error. Service.sync(); do_check_eq(hmacErrorCount, 1); _("Keys now: " + Service.collectionKeys.keyForCollection("history").keyPair); // And look! We downloaded history! let store = Service.engineManager.get("history")._store; - do_check_true(store.urlExists("http://foo/bar?record-no--0")); - do_check_true(store.urlExists("http://foo/bar?record-no--1")); - do_check_true(store.urlExists("http://foo/bar?record-no--2")); - do_check_true(store.urlExists("http://foo/bar?record-no--3")); - do_check_true(store.urlExists("http://foo/bar?record-no--4")); + do_check_true(yield promiseIsURIVisited("http://foo/bar?record-no--0")); + do_check_true(yield promiseIsURIVisited("http://foo/bar?record-no--1")); + do_check_true(yield promiseIsURIVisited("http://foo/bar?record-no--2")); + do_check_true(yield promiseIsURIVisited("http://foo/bar?record-no--3")); + do_check_true(yield promiseIsURIVisited("http://foo/bar?record-no--4")); do_check_eq(hmacErrorCount, 1); _("Busting some new server values."); // Now what happens if we corrupt the HMAC on the server? for (let i = 5; i < 10; i++) { let id = 'record-no--' + i; let modified = 1 + (Date.now() / 1000); @@ -183,26 +184,43 @@ add_test(function test_locally_changed_k Service.lastHMACEvent = 0; _("Syncing..."); Service.sync(); _("Keys now: " + Service.collectionKeys.keyForCollection("history").keyPair); _("Server keys have been updated, and we skipped over 5 more HMAC errors without adjusting history."); do_check_true(johndoe.modified("crypto") > old_key_time); do_check_eq(hmacErrorCount, 6); - do_check_false(store.urlExists("http://foo/bar?record-no--5")); - do_check_false(store.urlExists("http://foo/bar?record-no--6")); - do_check_false(store.urlExists("http://foo/bar?record-no--7")); - do_check_false(store.urlExists("http://foo/bar?record-no--8")); - do_check_false(store.urlExists("http://foo/bar?record-no--9")); - + do_check_false(yield promiseIsURIVisited("http://foo/bar?record-no--5")); + do_check_false(yield promiseIsURIVisited("http://foo/bar?record-no--6")); + do_check_false(yield promiseIsURIVisited("http://foo/bar?record-no--7")); + do_check_false(yield promiseIsURIVisited("http://foo/bar?record-no--8")); + do_check_false(yield promiseIsURIVisited("http://foo/bar?record-no--9")); } finally { Svc.Prefs.resetBranch(""); - server.stop(run_next_test); + let deferred = Promise.defer(); + server.stop(deferred.resolve); + yield deferred.promise; } }); function run_test() { let logger = Log4Moz.repository.rootLogger; Log4Moz.repository.rootLogger.addAppender(new Log4Moz.DumpAppender()); run_next_test(); } + +/** + * Asynchronously check a url is visited. + * @param url the url + * @return {Promise} + * @resolves When the check has been added successfully. + * @rejects JavaScript exception. + */ +function promiseIsURIVisited(url) { + let deferred = Promise.defer(); + PlacesUtils.asyncHistory.isURIVisited(Utils.makeURI(url), function(aURI, aIsVisited) { + deferred.resolve(aIsVisited); + }); + + return deferred.promise; +}
--- a/toolkit/modules/Sqlite.jsm +++ b/toolkit/modules/Sqlite.jsm @@ -142,48 +142,57 @@ function openConnection(options) { * @param basename * (string) The basename of this database name. Used for logging. * @param number * (Number) The connection number to this database. */ function OpenedConnection(connection, basename, number) { let log = Log4Moz.repository.getLogger("Sqlite.Connection." + basename); + // getLogger() returns a shared object. We can't modify the functions on this + // object since they would have effect on all instances and last write would + // win. So, we create a "proxy" object with our custom functions. Everything + // else is proxied back to the shared logger instance via prototype + // inheritance. + let logProxy = {__proto__: log}; + // Automatically prefix all log messages with the identifier. for (let level in Log4Moz.Level) { if (level == "Desc") { continue; } let lc = level.toLowerCase(); - log[lc] = function (msg) { - return Log4Moz.Logger.prototype[lc].call(log, "Conn #" + number + ": " + msg); - } + logProxy[lc] = function (msg) { + return log[lc].call(log, "Conn #" + number + ": " + msg); + }; } - this._log = log; + this._log = logProxy; this._log.info("Opened"); this._connection = connection; this._open = true; this._cachedStatements = new Map(); this._anonymousStatements = new Map(); this._anonymousCounter = 0; this._inProgressStatements = new Map(); this._inProgressCounter = 0; this._inProgressTransaction = null; } OpenedConnection.prototype = Object.freeze({ - TRANSACTION_DEFERRED: Ci.mozIStorageConnection.TRANSACTION_DEFERRED, - TRANSACTION_IMMEDIATE: Ci.mozIStorageConnection.TRANSACTION_IMMEDIATE, - TRANSACTION_EXCLUSIVE: Ci.mozIStorageConnection.TRANSACTION_EXCLUSIVE, + TRANSACTION_DEFERRED: "DEFERRED", + TRANSACTION_IMMEDIATE: "IMMEDIATE", + TRANSACTION_EXCLUSIVE: "EXCLUSIVE", + + TRANSACTION_TYPES: ["DEFERRED", "IMMEDIATE", "EXCLUSIVE"], get connectionReady() { return this._open && this._connection.connectionReady; }, /** * The row ID from the last INSERT operation. * @@ -244,31 +253,45 @@ OpenedConnection.prototype = Object.free * * @return Promise<> */ close: function () { if (!this._connection) { return Promise.resolve(); } - this._log.debug("Closing."); + this._log.debug("Request to close connection."); + + let deferred = Promise.defer(); - // Abort in-progress transaction. - if (this._inProgressTransaction) { - this._log.warn("Transaction in progress at time of close."); - try { - this._connection.rollbackTransaction(); - } catch (ex) { - this._log.warn("Error rolling back transaction: " + - CommonUtils.exceptionStr(ex)); - } - this._inProgressTransaction.reject(new Error("Connection being closed.")); - this._inProgressTransaction = null; + // We need to take extra care with transactions during shutdown. + // + // If we don't have a transaction in progress, we can proceed with shutdown + // immediately. + if (!this._inProgressTransaction) { + this._finalize(deferred); + return deferred.promise; } + // Else if we do have a transaction in progress, we forcefully roll it + // back. This is an async task, so we wait on it to finish before + // performing finalization. + this._log.warn("Transaction in progress at time of close. Rolling back."); + + let onRollback = this._finalize.bind(this, deferred); + + this.execute("ROLLBACK TRANSACTION").then(onRollback, onRollback); + this._inProgressTransaction.reject(new Error("Connection being closed.")); + this._inProgressTransaction = null; + + return deferred.promise; + }, + + _finalize: function (deferred) { + this._log.debug("Finalizing connection."); // Cancel any in-progress statements. for (let [k, statement] of this._inProgressStatements) { statement.cancel(); } this._inProgressStatements.clear(); // Next we finalize all active statements. for (let [k, statement] of this._anonymousStatements) { @@ -280,28 +303,24 @@ OpenedConnection.prototype = Object.free statement.finalize(); } this._cachedStatements.clear(); // This guards against operations performed between the call to this // function and asyncClose() finishing. See also bug 726990. this._open = false; - let deferred = Promise.defer(); - this._log.debug("Calling asyncClose()."); this._connection.asyncClose({ complete: function () { this._log.info("Closed"); this._connection = null; deferred.resolve(); }.bind(this), }); - - return deferred.promise; }, /** * Execute a SQL statement and cache the underlying statement object. * * This function executes a SQL statement and also caches the underlying * derived statement object so subsequent executions are faster and use * less resources. @@ -418,17 +437,17 @@ OpenedConnection.prototype = Object.free return deferred.promise; }, /** * Whether a transaction is currently in progress. */ get transactionInProgress() { - return this._open && this._connection.transactionInProgress; + return this._open && !!this._inProgressTransaction; }, /** * Perform a transaction. * * A transaction is specified by a user-supplied function that is a * generator function which can be used by Task.jsm's Task.spawn(). The * function receives this connection instance as its argument. @@ -445,44 +464,86 @@ OpenedConnection.prototype = Object.free * the transaction is rolled back, the promise is rejected. * * @param func * (function) What to perform as part of the transaction. * @param type optional * One of the TRANSACTION_* constants attached to this type. */ executeTransaction: function (func, type=this.TRANSACTION_DEFERRED) { + if (this.TRANSACTION_TYPES.indexOf(type) == -1) { + throw new Error("Unknown transaction type: " + type); + } + this._ensureOpen(); - if (this.transactionInProgress) { + if (this._inProgressTransaction) { throw new Error("A transaction is already active. Only one transaction " + "can be active at a time."); } this._log.debug("Beginning transaction"); - this._connection.beginTransactionAs(type); - let deferred = Promise.defer(); this._inProgressTransaction = deferred; + Task.spawn(function doTransaction() { + // It's tempting to not yield here and rely on the implicit serial + // execution of issued statements. However, the yield serves an important + // purpose: catching errors in statement execution. + yield this.execute("BEGIN " + type + " TRANSACTION"); - Task.spawn(func(this)).then( - function onSuccess (result) { - this._connection.commitTransaction(); + let result; + try { + result = yield Task.spawn(func(this)); + } catch (ex) { + // It's possible that a request to close the connection caused the + // error. + // Assertion: close() will unset this._inProgressTransaction when + // called. + if (!this._inProgressTransaction) { + this._log.warn("Connection was closed while performing transaction. " + + "Received error should be due to closed connection: " + + CommonUtils.exceptionStr(ex)); + throw ex; + } + + this._log.warn("Error during transaction. Rolling back: " + + CommonUtils.exceptionStr(ex)); + try { + yield this.execute("ROLLBACK TRANSACTION"); + } catch (inner) { + this._log.warn("Could not roll back transaction. This is weird: " + + CommonUtils.exceptionStr(inner)); + } + + throw ex; + } + + // See comment above about connection being closed during transaction. + if (!this._inProgressTransaction) { + this._log.warn("Connection was closed while performing transaction. " + + "Unable to commit."); + throw new Error("Connection closed before transaction committed."); + } + + try { + yield this.execute("COMMIT TRANSACTION"); + } catch (ex) { + this._log.warn("Error committing transaction: " + + CommonUtils.exceptionStr(ex)); + throw ex; + } + + throw new Task.Result(result); + }.bind(this)).then( + function onSuccess(result) { this._inProgressTransaction = null; - this._log.debug("Transaction committed."); - deferred.resolve(result); }.bind(this), - - function onError (error) { - this._log.warn("Error during transaction. Rolling back: " + - CommonUtils.exceptionStr(error)); - this._connection.rollbackTransaction(); + function onError(error) { this._inProgressTransaction = null; - deferred.reject(error); }.bind(this) ); return deferred.promise; }, /**
--- a/toolkit/modules/tests/xpcshell/test_sqlite.js +++ b/toolkit/modules/tests/xpcshell/test_sqlite.js @@ -208,16 +208,32 @@ add_task(function test_on_row_stop_itera }); do_check_null(result); do_check_eq(i, 5); yield c.close(); }); +add_task(function test_invalid_transaction_type() { + let c = yield getDummyDatabase("invalid_transaction_type"); + + let errored = false; + try { + c.executeTransaction(function () {}, "foobar"); + } catch (ex) { + errored = true; + do_check_true(ex.message.startsWith("Unknown transaction type")); + } finally { + do_check_true(errored); + } + + yield c.close(); +}); + add_task(function test_execute_transaction_success() { let c = yield getDummyDatabase("execute_transaction_success"); do_check_false(c.transactionInProgress); yield c.executeTransaction(function transaction(conn) { do_check_eq(c, conn); do_check_true(conn.transactionInProgress); @@ -252,8 +268,58 @@ add_task(function test_execute_transacti yield deferred.promise; let rows = yield c.execute("SELECT * FROM dirs"); do_check_eq(rows.length, 0); yield c.close(); }); +add_task(function test_close_during_transaction() { + let c = yield getDummyDatabase("close_during_transaction"); + + yield c.execute("INSERT INTO dirs (path) VALUES ('foo')"); + + let errored = false; + try { + yield c.executeTransaction(function transaction(conn) { + yield c.execute("INSERT INTO dirs (path) VALUES ('bar')"); + yield c.close(); + }); + } catch (ex) { + errored = true; + do_check_eq(ex.message, "Connection being closed."); + } finally { + do_check_true(errored); + } + + let c2 = yield getConnection("close_during_transaction"); + let rows = yield c2.execute("SELECT * FROM dirs"); + do_check_eq(rows.length, 1); + + yield c2.close(); +}); + +add_task(function test_detect_multiple_transactions() { + let c = yield getDummyDatabase("detect_multiple_transactions"); + + yield c.executeTransaction(function main() { + yield c.execute("INSERT INTO dirs (path) VALUES ('foo')"); + + let errored = false; + try { + yield c.executeTransaction(function child() { + yield c.execute("INSERT INTO dirs (path) VALUES ('bar')"); + }); + } catch (ex) { + errored = true; + do_check_true(ex.message.startsWith("A transaction is already active.")); + } finally { + do_check_true(errored); + } + }); + + let rows = yield c.execute("SELECT * FROM dirs"); + do_check_eq(rows.length, 1); + + yield c.close(); +}); +