Merge fx-team to m-c.

This commit is contained in:
Ryan VanderMeulen 2014-04-24 13:39:46 -04:00
commit 31d6244ff0
44 changed files with 716 additions and 416 deletions

View File

@ -161,8 +161,7 @@ CustomizeMode.prototype = {
let toolbarVisibilityBtn = document.getElementById(kToolbarVisibilityBtn);
let togglableToolbars = window.getTogglableToolbars();
let bookmarksToolbar = document.getElementById("PersonalToolbar");
if (togglableToolbars.length == 0 ||
(togglableToolbars.length == 1 && togglableToolbars[0] == bookmarksToolbar)) {
if (togglableToolbars.length == 0) {
toolbarVisibilityBtn.setAttribute("hidden", "true");
} else {
toolbarVisibilityBtn.removeAttribute("hidden");

View File

@ -97,3 +97,19 @@ function*() {
is(elt.textContent, res2[i].value, res2[i].selector + " has the right value after style update.");
}
});
addTest("Test that long labels on left/right are rotated 90 degrees",
function*() {
let viewdoc = view.document;
const LONG_TEXT_ROTATE_LIMIT = 3;
for (let i = 0; i < res1.length; i++) {
let elt = viewdoc.querySelector(res1[i].selector);
let isLong = elt.textContent.length > LONG_TEXT_ROTATE_LIMIT;
let classList = elt.parentNode.classList
let canBeRotated = classList.contains("left") || classList.contains("right");
let isRotated = classList.contains("rotate");
is(canBeRotated && isLong, isRotated, res1[i].selector + " correctly rotated.");
}
});

View File

@ -174,6 +174,14 @@ body {
left: 0;
}
.rotate.left:not(.editing) {
transform: rotate(-90deg);
}
.rotate.right:not(.editing) {
transform: rotate(90deg);
}
.tooltip {
position: absolute;
bottom: 0;

View File

@ -20,6 +20,7 @@ const {InplaceEditor, editableItem} = devtools.require("devtools/shared/inplace-
const {parseDeclarations} = devtools.require("devtools/styleinspector/css-parsing-utils");
const NUMERIC = /^-?[\d\.]+$/;
const LONG_TEXT_ROTATE_LIMIT = 3;
/**
* An instance of EditingSession tracks changes that have been made during the
@ -223,10 +224,14 @@ LayoutView.prototype = {
let session = new EditingSession(document, this.elementRules);
let initialValue = session.getProperty(realProperty);
new InplaceEditor({
let editor = new InplaceEditor({
element: element,
initial: initialValue,
start: (editor) => {
editor.elt.parentNode.classList.add("editing");
},
change: (value) => {
if (NUMERIC.test(value))
value += "px";
@ -245,6 +250,7 @@ LayoutView.prototype = {
},
done: (value, commit) => {
editor.elt.parentNode.classList.remove("editing");
if (!commit)
session.revert();
}
@ -387,6 +393,7 @@ LayoutView.prototype = {
continue;
}
span.textContent = this.map[i].value;
this.manageOverflowingText(span);
}
width -= this.map.borderLeft.value + this.map.borderRight.value +
@ -420,6 +427,15 @@ LayoutView.prototype = {
toolbox.highlighterUtils.unhighlight();
},
manageOverflowingText: function(span) {
let classList = span.parentNode.classList;
if (classList.contains("left") || classList.contains("right")) {
let force = span.textContent.length > LONG_TEXT_ROTATE_LIMIT;
classList.toggle("rotate", force);
}
}
};
let elts;

View File

@ -193,13 +193,13 @@ function createAndLoadTemporaryFile(aFile, aFileName, aFileContent)
{
// Create a temporary file.
aFile = FileUtils.getFile("TmpD", [aFileName]);
aFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
aFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
// Write the temporary file.
let fout = Cc["@mozilla.org/network/file-output-stream;1"].
createInstance(Ci.nsIFileOutputStream);
fout.init(aFile.QueryInterface(Ci.nsILocalFile), 0x02 | 0x08 | 0x20,
0644, fout.DEFER_OPEN);
0o644, fout.DEFER_OPEN);
gScratchpad.setFilename(aFile.path);
gScratchpad.importFromFile(aFile.QueryInterface(Ci.nsILocalFile), true,

View File

@ -45,21 +45,21 @@ function runTests()
// Create a temporary file.
gFileA = FileUtils.getFile("TmpD", ["fileAForBug684546.tmp"]);
gFileA.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
gFileA.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
gFileB = FileUtils.getFile("TmpD", ["fileBForBug684546.tmp"]);
gFileB.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
gFileB.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
// Write the temporary file.
let foutA = Cc["@mozilla.org/network/file-output-stream;1"].
createInstance(Ci.nsIFileOutputStream);
foutA.init(gFileA.QueryInterface(Ci.nsILocalFile), 0x02 | 0x08 | 0x20,
0644, foutA.DEFER_OPEN);
0o644, foutA.DEFER_OPEN);
let foutB = Cc["@mozilla.org/network/file-output-stream;1"].
createInstance(Ci.nsIFileOutputStream);
foutB.init(gFileB.QueryInterface(Ci.nsILocalFile), 0x02 | 0x08 | 0x20,
0644, foutB.DEFER_OPEN);
0o644, foutB.DEFER_OPEN);
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
createInstance(Ci.nsIScriptableUnicodeConverter);

View File

@ -98,13 +98,13 @@ function createAndLoadTemporaryFile()
{
// Create a temporary file.
gFile = FileUtils.getFile("TmpD", [gFileName]);
gFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
gFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
// Write the temporary file.
let fout = Cc["@mozilla.org/network/file-output-stream;1"].
createInstance(Ci.nsIFileOutputStream);
fout.init(gFile.QueryInterface(Ci.nsILocalFile), 0x02 | 0x08 | 0x20,
0644, fout.DEFER_OPEN);
0o644, fout.DEFER_OPEN);
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
createInstance(Ci.nsIScriptableUnicodeConverter);

View File

@ -1336,6 +1336,8 @@ public final class HomeConfig {
mConfigOrder.remove(panelId);
mConfigOrder.add(destIndex, panelId);
mHasChanged = true;
return true;
}

View File

@ -4494,7 +4494,7 @@ Tab.prototype = {
// call above does so at the end of the updateViewportSize function. As long
// as that is happening, we don't need to do it again here.
if (contentDocument.mozSyntheticDocument) {
if (!this.restoredSessionZoom() && contentDocument.mozSyntheticDocument) {
// for images, scale to fit width. this needs to happen *after* the call
// to updateMetadata above, because that call sets the CSS viewport which
// will affect the page size (i.e. contentDocument.body.scroll*) that we

View File

@ -70,7 +70,8 @@ interface nsIPrefBranch : nsISupports
* @param aPrefName The boolean preference to set the state of.
* @param aValue The boolean value to set the preference to.
*
* @throws Error if setting failed or the value is the wrong type.
* @throws Error if setting failed or the preference has a default
value of a type other than boolean.
*
* @see getBoolPref
*/
@ -106,7 +107,8 @@ interface nsIPrefBranch : nsISupports
* @param aPrefName The string preference to set.
* @param aValue The string value to set the preference to.
*
* @throws Error if setting failed or the value is the wrong type.
* @throws Error if setting failed or the preference has a default
value of a type other than string.
*
* @see getCharPref
*/
@ -129,7 +131,8 @@ interface nsIPrefBranch : nsISupports
* @param aPrefName The integer preference to set the value of.
* @param aValue The integer value to set the preference to.
*
* @throws Error if setting failed or the value is the wrong type.
* @throws Error if setting failed or the preference has a default
value of a type other than integer.
*
* @see getIntPref
*/

View File

@ -706,19 +706,24 @@ static bool pref_ValueChanged(PrefValue oldValue, PrefValue newValue, PrefType t
return changed;
}
static void pref_SetValue(PrefValue* oldValue, PrefValue newValue, PrefType type)
/*
* Overwrite the type and value of an existing preference. Caller must
* ensure that they are not changing the type of a preference that has
* a default value.
*/
static void pref_SetValue(PrefValue* existingValue, uint16_t *existingFlags,
PrefValue newValue, PrefType newType)
{
switch (type & PREF_VALUETYPE_MASK)
{
case PREF_STRING:
PR_ASSERT(newValue.stringVal);
if (oldValue->stringVal)
PL_strfree(oldValue->stringVal);
oldValue->stringVal = newValue.stringVal ? PL_strdup(newValue.stringVal) : nullptr;
break;
default:
*oldValue = newValue;
if ((*existingFlags & PREF_STRING) && existingValue->stringVal) {
PL_strfree(existingValue->stringVal);
}
*existingFlags = (*existingFlags & ~PREF_VALUETYPE_MASK) | newType;
if (newType & PREF_STRING) {
PR_ASSERT(newValue.stringVal);
existingValue->stringVal = newValue.stringVal ? PL_strdup(newValue.stringVal) : nullptr;
}
else {
*existingValue = newValue;
}
gDirty = true;
}
@ -752,7 +757,7 @@ nsresult pref_HashPref(const char *key, PrefValue value, PrefType type, uint32_t
if (!pref)
return NS_ERROR_OUT_OF_MEMORY;
// new entry, better intialize
// new entry, better initialize
if (!pref->key) {
// initialize the pref entry
@ -761,10 +766,9 @@ nsresult pref_HashPref(const char *key, PrefValue value, PrefType type, uint32_t
memset(&pref->defaultPref, 0, sizeof(pref->defaultPref));
memset(&pref->userPref, 0, sizeof(pref->userPref));
}
else if ((((PrefType)(pref->flags)) & PREF_VALUETYPE_MASK) !=
(type & PREF_VALUETYPE_MASK))
else if ((pref->flags & PREF_HAS_DEFAULT) && PREF_TYPE(pref) != type)
{
NS_WARNING(nsPrintfCString("Trying to set pref %s to with the wrong type!", key).get());
NS_WARNING(nsPrintfCString("Trying to overwrite value of default pref %s with the wrong type!", key).get());
return NS_ERROR_UNEXPECTED;
}
@ -776,7 +780,7 @@ nsresult pref_HashPref(const char *key, PrefValue value, PrefType type, uint32_t
if (pref_ValueChanged(pref->defaultPref, value, type) ||
!(pref->flags & PREF_HAS_DEFAULT))
{
pref_SetValue(&pref->defaultPref, value, type);
pref_SetValue(&pref->defaultPref, &pref->flags, value, type);
pref->flags |= PREF_HAS_DEFAULT;
if (!PREF_HAS_USER_VALUE(pref))
valueChanged = true;
@ -787,21 +791,23 @@ nsresult pref_HashPref(const char *key, PrefValue value, PrefType type, uint32_t
{
/* If new value is same as the default value, then un-set the user value.
Otherwise, set the user value only if it has changed */
if (!pref_ValueChanged(pref->defaultPref, value, type) &&
(pref->flags & PREF_HAS_DEFAULT) &&
if ((pref->flags & PREF_HAS_DEFAULT) &&
!pref_ValueChanged(pref->defaultPref, value, type) &&
!(flags & kPrefForceSet))
{
if (PREF_HAS_USER_VALUE(pref))
{
/* XXX should we free a user-set string value if there is one? */
pref->flags &= ~PREF_USERSET;
if (!PREF_IS_LOCKED(pref))
valueChanged = true;
}
}
else if ( !PREF_HAS_USER_VALUE(pref) ||
pref_ValueChanged(pref->userPref, value, type) )
else if (!PREF_HAS_USER_VALUE(pref) ||
PREF_TYPE(pref) != type ||
pref_ValueChanged(pref->userPref, value, type) )
{
pref_SetValue(&pref->userPref, value, type);
pref_SetValue(&pref->userPref, &pref->flags, value, type);
pref->flags |= PREF_USERSET;
if (!PREF_IS_LOCKED(pref))
valueChanged = true;

View File

@ -0,0 +1,63 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* Tests for changing the type of a preference (bug 985998) */
const PREF_INVALID = 0;
const PREF_BOOL = 128;
const PREF_INT = 64;
const PREF_STRING = 32;
function run_test() {
var ps = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefService);
let defaultBranch = ps.getDefaultBranch("");
let userBranch = ps.getBranch("");
//**************************************************************************//
// Can't change the type of prefs that have default values
defaultBranch.setBoolPref("TypeTest.existing.bool", true);
defaultBranch.setIntPref("TypeTest.existing.int", 23);
defaultBranch.setCharPref("TypeTest.existing.char", "hey");
// The user branch reads back the expected default
do_check_eq(userBranch.getBoolPref("TypeTest.existing.bool"), true);
do_check_eq(userBranch.getIntPref("TypeTest.existing.int"), 23);
do_check_eq(userBranch.getCharPref("TypeTest.existing.char"), "hey");
// All the combinations of attempted type changes
do_check_throws(function() {
userBranch.setCharPref("TypeTest.existing.bool", "boo"); }, Cr.NS_ERROR_UNEXPECTED);
do_check_throws(function() {
userBranch.setIntPref("TypeTest.existing.bool", 5); }, Cr.NS_ERROR_UNEXPECTED);
do_check_throws(function() {
userBranch.setCharPref("TypeTest.existing.int", "boo"); }, Cr.NS_ERROR_UNEXPECTED);
do_check_throws(function() {
userBranch.setBoolPref("TypeTest.existing.int", true); }, Cr.NS_ERROR_UNEXPECTED);
do_check_throws(function() {
userBranch.setBoolPref("TypeTest.existing.char", true); }, Cr.NS_ERROR_UNEXPECTED);
do_check_throws(function() {
userBranch.setIntPref("TypeTest.existing.char", 6); }, Cr.NS_ERROR_UNEXPECTED);
//**************************************************************************//
// Prefs that don't have default values can mutate
let pref = "TypeTest.user";
userBranch.setBoolPref(pref, true);
userBranch.setCharPref(pref, "yay");
do_check_eq(userBranch.getCharPref(pref), "yay");
userBranch.setIntPref(pref, 7);
do_check_eq(userBranch.getIntPref(pref), 7);
userBranch.setBoolPref(pref, false);
do_check_eq(userBranch.getBoolPref(pref), false);
userBranch.setIntPref(pref, 8);
do_check_eq(userBranch.getIntPref(pref), 8);
userBranch.setCharPref(pref, "whee");
do_check_eq(userBranch.getCharPref(pref), "whee");
userBranch.setBoolPref(pref, true);
do_check_eq(userBranch.getBoolPref(pref), true);
}

View File

@ -10,5 +10,6 @@ support-files =
[test_bug506224.js]
[test_bug577950.js]
[test_bug790374.js]
[test_changeType.js]
[test_extprefs.js]
[test_libPrefs.js]

View File

@ -194,8 +194,8 @@ StorageBaseStatementInternal::ExecuteAsync(
NS_ENSURE_TRUE(stmts.AppendElement(data), NS_ERROR_OUT_OF_MEMORY);
// Dispatch to the background
return AsyncExecuteStatements::execute(stmts, mDBConnection, aCallback,
_stmt);
return AsyncExecuteStatements::execute(stmts, mDBConnection,
mNativeConnection, aCallback, _stmt);
}
NS_IMETHODIMP

View File

@ -11,6 +11,7 @@
#include "nsCOMPtr.h"
#include "nsAutoPtr.h"
struct sqlite3;
struct sqlite3_stmt;
class mozIStorageError;
class mozIStorageBindingParamsArray;
@ -97,6 +98,7 @@ protected: // mix-in bits are protected
StorageBaseStatementInternal();
nsRefPtr<Connection> mDBConnection;
sqlite3 *mNativeConnection;
/**
* Our asynchronous statement.

View File

@ -131,13 +131,15 @@ AsyncStatement::AsyncStatement()
nsresult
AsyncStatement::initialize(Connection *aDBConnection,
sqlite3 *aNativeConnection,
const nsACString &aSQLStatement)
{
NS_ASSERTION(aDBConnection, "No database connection given!");
NS_ASSERTION(aDBConnection->GetNativeConnection(),
"We should never be called with a null sqlite3 database!");
MOZ_ASSERT(aDBConnection, "No database connection given!");
MOZ_ASSERT(!aDBConnection->isClosed(), "Database connection should be valid");
MOZ_ASSERT(aNativeConnection, "No native connection given!");
mDBConnection = aDBConnection;
mNativeConnection = aNativeConnection;
mSQLString = aSQLStatement;
PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Inited async statement '%s' (0x%p)",
@ -300,11 +302,12 @@ AsyncStatement::getAsyncStatement(sqlite3_stmt **_stmt)
#endif
if (!mAsyncStatement) {
int rc = mDBConnection->prepareStatement(mSQLString, &mAsyncStatement);
int rc = mDBConnection->prepareStatement(mNativeConnection, mSQLString,
&mAsyncStatement);
if (rc != SQLITE_OK) {
PR_LOG(gStorageLog, PR_LOG_ERROR,
("Sqlite statement prepare error: %d '%s'", rc,
::sqlite3_errmsg(mDBConnection->GetNativeConnection())));
::sqlite3_errmsg(mNativeConnection)));
PR_LOG(gStorageLog, PR_LOG_ERROR,
("Statement was: '%s'", mSQLString.get()));
*_stmt = nullptr;

View File

@ -45,10 +45,13 @@ public:
*
* @param aDBConnection
* The Connection object this statement is associated with.
* @param aNativeConnection
* The native Sqlite connection this statement is associated with.
* @param aSQLStatement
* The SQL statement to prepare that this object will represent.
*/
nsresult initialize(Connection *aDBConnection,
sqlite3 *aNativeConnection,
const nsACString &aSQLStatement);
/**

View File

@ -163,12 +163,14 @@ private:
nsresult
AsyncExecuteStatements::execute(StatementDataArray &aStatements,
Connection *aConnection,
sqlite3 *aNativeConnection,
mozIStorageStatementCallback *aCallback,
mozIStoragePendingStatement **_stmt)
{
// Create our event to run in the background
nsRefPtr<AsyncExecuteStatements> event =
new AsyncExecuteStatements(aStatements, aConnection, aCallback);
new AsyncExecuteStatements(aStatements, aConnection, aNativeConnection,
aCallback);
NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
// Dispatch it to the background
@ -194,9 +196,11 @@ AsyncExecuteStatements::execute(StatementDataArray &aStatements,
AsyncExecuteStatements::AsyncExecuteStatements(StatementDataArray &aStatements,
Connection *aConnection,
sqlite3 *aNativeConnection,
mozIStorageStatementCallback *aCallback)
: mConnection(aConnection)
, mTransactionManager(nullptr)
, mNativeConnection(aNativeConnection)
, mHasTransaction(false)
, mCallback(aCallback)
, mCallingThread(::do_GetCurrentThread())
, mMaxWait(TimeDuration::FromMilliseconds(MAX_MILLISECONDS_BETWEEN_RESULTS))
@ -212,6 +216,11 @@ AsyncExecuteStatements::AsyncExecuteStatements(StatementDataArray &aStatements,
NS_IF_ADDREF(mCallback);
}
AsyncExecuteStatements::~AsyncExecuteStatements()
{
MOZ_ASSERT(!mHasTransaction, "There should be no transaction at this point");
}
bool
AsyncExecuteStatements::shouldNotify()
{
@ -333,7 +342,7 @@ AsyncExecuteStatements::executeStatement(sqlite3_stmt *aStatement)
// lock the sqlite mutex so sqlite3_errmsg cannot change
SQLiteMutexAutoLock lockedScope(mDBMutex);
int rc = mConnection->stepStatement(aStatement);
int rc = mConnection->stepStatement(mNativeConnection, aStatement);
// Stop if we have no more results.
if (rc == SQLITE_DONE)
{
@ -364,8 +373,9 @@ AsyncExecuteStatements::executeStatement(sqlite3_stmt *aStatement)
// Construct the error message before giving up the mutex (which we cannot
// hold during the call to notifyError).
sqlite3 *db = mConnection->GetNativeConnection();
nsCOMPtr<mozIStorageError> errorObj(new Error(rc, ::sqlite3_errmsg(db)));
nsCOMPtr<mozIStorageError> errorObj(
new Error(rc, ::sqlite3_errmsg(mNativeConnection))
);
// We cannot hold the DB mutex while calling notifyError.
SQLiteMutexAutoUnlock unlockedScope(mDBMutex);
(void)notifyError(errorObj);
@ -433,9 +443,9 @@ AsyncExecuteStatements::notifyComplete()
mStatements.Clear();
// Handle our transaction, if we have one
if (mTransactionManager) {
if (mHasTransaction) {
if (mState == COMPLETED) {
nsresult rv = mTransactionManager->Commit();
nsresult rv = mConnection->commitTransactionInternal(mNativeConnection);
if (NS_FAILED(rv)) {
mState = ERROR;
(void)notifyError(mozIStorageError::ERROR,
@ -443,10 +453,9 @@ AsyncExecuteStatements::notifyComplete()
}
}
else {
(void)mTransactionManager->Rollback();
NS_WARN_IF(NS_FAILED(mConnection->rollbackTransactionInternal(mNativeConnection)));
}
delete mTransactionManager;
mTransactionManager = nullptr;
mHasTransaction = false;
}
// Always generate a completion notification; it is what guarantees that our
@ -563,6 +572,8 @@ AsyncExecuteStatements::Cancel()
NS_IMETHODIMP
AsyncExecuteStatements::Run()
{
MOZ_ASSERT(!mConnection->isClosed());
// Do not run if we have been canceled.
{
MutexAutoLock lockedScope(mMutex);
@ -574,8 +585,15 @@ AsyncExecuteStatements::Run()
if (statementsNeedTransaction()) {
Connection* rawConnection = static_cast<Connection*>(mConnection.get());
mTransactionManager = new mozStorageAsyncTransaction(rawConnection, false,
mozIStorageConnection::TRANSACTION_IMMEDIATE);
if (NS_SUCCEEDED(mConnection->beginTransactionInternal(mNativeConnection,
mozIStorageConnection::TRANSACTION_IMMEDIATE))) {
mHasTransaction = true;
}
#ifdef DEBUG
else {
NS_WARNING("Unable to create a transaction for async execution.");
}
#endif
}
// Execute each statement, giving the callback results if it returns any.
@ -592,9 +610,8 @@ AsyncExecuteStatements::Run()
mState = ERROR;
// Build the error object; can't call notifyError with the lock held
sqlite3 *db = mConnection->GetNativeConnection();
nsCOMPtr<mozIStorageError> errorObj(
new Error(rc, ::sqlite3_errmsg(db))
new Error(rc, ::sqlite3_errmsg(mNativeConnection))
);
{
// We cannot hold the DB mutex and call notifyError.

View File

@ -29,14 +29,6 @@ class Connection;
class ResultSet;
class StatementData;
/**
* An instance of the mozStorageTransaction<> family dedicated
* to concrete class |Connection|.
*/
typedef mozStorageTransactionBase<mozilla::storage::Connection,
nsRefPtr<mozilla::storage::Connection> >
mozStorageAsyncTransaction;
class AsyncExecuteStatements MOZ_FINAL : public nsIRunnable
, public mozIStoragePendingStatement
{
@ -66,6 +58,8 @@ public:
* Ownership is transfered from the caller.
* @param aConnection
* The connection that created the statements to execute.
* @param aNativeConnection
* The native Sqlite connection that created the statements to execute.
* @param aCallback
* The callback that is notified of results, completion, and errors.
* @param _stmt
@ -73,6 +67,7 @@ public:
*/
static nsresult execute(StatementDataArray &aStatements,
Connection *aConnection,
sqlite3 *aNativeConnection,
mozIStorageStatementCallback *aCallback,
mozIStoragePendingStatement **_stmt);
@ -90,7 +85,9 @@ public:
private:
AsyncExecuteStatements(StatementDataArray &aStatements,
Connection *aConnection,
sqlite3 *aNativeConnection,
mozIStorageStatementCallback *aCallback);
~AsyncExecuteStatements();
/**
* Binds and then executes a given statement until completion, an error
@ -187,7 +184,8 @@ private:
StatementDataArray mStatements;
nsRefPtr<Connection> mConnection;
mozStorageAsyncTransaction *mTransactionManager;
sqlite3 *mNativeConnection;
bool mHasTransaction;
mozIStorageStatementCallback *mCallback;
nsCOMPtr<nsIThread> mCallingThread;
nsRefPtr<ResultSet> mResultSet;

View File

@ -333,9 +333,11 @@ class AsyncCloseConnection MOZ_FINAL: public nsRunnable
{
public:
AsyncCloseConnection(Connection *aConnection,
sqlite3 *aNativeConnection,
nsIRunnable *aCallbackEvent,
already_AddRefed<nsIThread> aAsyncExecutionThread)
: mConnection(aConnection)
, mNativeConnection(aNativeConnection)
, mCallbackEvent(aCallbackEvent)
, mAsyncExecutionThread(aAsyncExecutionThread)
{
@ -351,7 +353,7 @@ public:
#endif // DEBUG
// Internal close.
(void)mConnection->internalClose();
(void)mConnection->internalClose(mNativeConnection);
// Callback
if (mCallbackEvent) {
@ -376,6 +378,7 @@ public:
}
private:
nsRefPtr<Connection> mConnection;
sqlite3 *mNativeConnection;
nsCOMPtr<nsIRunnable> mCallbackEvent;
nsCOMPtr<nsIThread> mAsyncExecutionThread;
};
@ -470,6 +473,7 @@ Connection::Connection(Service *aService,
, threadOpenedOn(do_GetCurrentThread())
, mDBConn(nullptr)
, mAsyncExecutionThreadShuttingDown(false)
, mConnectionClosed(false)
, mTransactionInProgress(false)
, mProgressHandler(nullptr)
, mFlags(aFlags)
@ -519,6 +523,18 @@ NS_IMETHODIMP_(MozExternalRefCountType) Connection::Release(void)
return count;
}
int32_t
Connection::getSqliteRuntimeStatus(int32_t aStatusOption, int32_t* aMaxValue)
{
MOZ_ASSERT(mDBConn, "A connection must exist at this point");
int curr = 0, max = 0;
DebugOnly<int> rc = ::sqlite3_db_status(mDBConn, aStatusOption, &curr, &max, 0);
MOZ_ASSERT(NS_SUCCEEDED(convertResultCode(rc)));
if (aMaxValue)
*aMaxValue = max;
return curr;
}
nsIEventTarget *
Connection::getAsyncExecutionTarget()
{
@ -654,7 +670,7 @@ Connection::initializeInternal(nsIFile* aDatabaseFile)
nsAutoCString cacheSizeQuery(MOZ_STORAGE_UNIQUIFY_QUERY_STR
"PRAGMA cache_size = ");
cacheSizeQuery.AppendInt(-MAX_CACHE_SIZE_KIBIBYTES);
int srv = executeSql(cacheSizeQuery.get());
int srv = executeSql(mDBConn, cacheSizeQuery.get());
if (srv != SQLITE_OK) {
::sqlite3_close(mDBConn);
mDBConn = nullptr;
@ -732,11 +748,11 @@ Connection::databaseElementExists(enum DatabaseElementType aElementType,
query.Append("'");
sqlite3_stmt *stmt;
int srv = prepareStatement(query, &stmt);
int srv = prepareStatement(mDBConn, query, &stmt);
if (srv != SQLITE_OK)
return convertResultCode(srv);
srv = stepStatement(stmt);
srv = stepStatement(mDBConn, stmt);
// we just care about the return value from step
(void)::sqlite3_finalize(stmt);
@ -801,31 +817,50 @@ Connection::setClosedState()
mAsyncExecutionThreadShuttingDown = true;
}
// Set the property to null before closing the connection, otherwise the other
// functions in the module may try to use the connection after it is closed.
mDBConn = nullptr;
return NS_OK;
}
bool
Connection::isClosing(bool aResultOnClosed) {
Connection::connectionReady()
{
return mDBConn != nullptr;
}
bool
Connection::isClosing()
{
bool shuttingDown = false;
{
MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
shuttingDown = mAsyncExecutionThreadShuttingDown;
}
return shuttingDown && !isClosed();
}
bool
Connection::isClosed()
{
MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
return mAsyncExecutionThreadShuttingDown &&
(aResultOnClosed || ConnectionReady());
return mConnectionClosed;
}
nsresult
Connection::internalClose()
Connection::internalClose(sqlite3 *aNativeConnection)
{
#ifdef DEBUG
// Sanity checks to make sure we are in the proper state before calling this.
NS_ASSERTION(mDBConn, "Database connection is already null!");
MOZ_ASSERT(aNativeConnection, "Database connection is invalid!");
MOZ_ASSERT(!isClosed());
#ifdef DEBUG
{ // Make sure we have marked our async thread as shutting down.
MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
NS_ASSERTION(mAsyncExecutionThreadShuttingDown,
"Did not call setClosedState!");
}
bool onOpeningThread = false;
(void)threadOpenedOn->IsOnCurrentThread(&onOpeningThread);
#endif // DEBUG
#ifdef PR_LOGGING
@ -836,25 +871,23 @@ Connection::internalClose()
leafName.get()));
#endif
// Set the property to null before closing the connection, otherwise the other
// functions in the module may try to use the connection after it is closed.
sqlite3 *dbConn = mDBConn;
mDBConn = nullptr;
// At this stage, we may still have statements that need to be
// finalized. Attempt to close the database connection. This will
// always disconnect any virtual tables and cleanly finalize their
// internal statements. Once this is done, closing may fail due to
// unfinalized client statements, in which case we need to finalize
// these statements and close again.
int srv = sqlite3_close(dbConn);
{
MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
mConnectionClosed = true;
}
int srv = sqlite3_close(aNativeConnection);
if (srv == SQLITE_BUSY) {
// We still have non-finalized statements. Finalize them.
sqlite3_stmt *stmt = nullptr;
while ((stmt = ::sqlite3_next_stmt(dbConn, stmt))) {
while ((stmt = ::sqlite3_next_stmt(aNativeConnection, stmt))) {
PR_LOG(gStorageLog, PR_LOG_NOTICE,
("Auto-finalizing SQL statement '%s' (%x)",
::sqlite3_sql(stmt),
@ -889,7 +922,7 @@ Connection::internalClose()
// Now that all statements have been finalized, we
// should be able to close.
srv = ::sqlite3_close(dbConn);
srv = ::sqlite3_close(aNativeConnection);
}
@ -912,23 +945,21 @@ Connection::getFilename()
}
int
Connection::stepStatement(sqlite3_stmt *aStatement)
Connection::stepStatement(sqlite3 *aNativeConnection, sqlite3_stmt *aStatement)
{
MOZ_ASSERT(aStatement);
bool checkedMainThread = false;
TimeStamp startTime = TimeStamp::Now();
// mDBConn may be null if the executing statement has been created and cached
// after a call to asyncClose() but before the connection has been nullified
// by internalClose(). In such a case closing the connection fails due to
// the existence of prepared statements, but mDBConn is set to null
// regardless. This usually happens when other tasks using cached statements
// are asynchronously scheduled for execution and any of them ends up after
// asyncClose. See bug 728653 for details.
if (!mDBConn)
// The connection may have been closed if the executing statement has been
// created and cached after a call to asyncClose() but before the actual
// sqlite3_close(). This usually happens when other tasks using cached
// statements are asynchronously scheduled for execution and any of them ends
// up after asyncClose. See bug 728653 for details.
if (isClosed())
return SQLITE_MISUSE;
(void)::sqlite3_extended_result_codes(mDBConn, 1);
(void)::sqlite3_extended_result_codes(aNativeConnection, 1);
int srv;
while ((srv = ::sqlite3_step(aStatement)) == SQLITE_LOCKED_SHAREDCACHE) {
@ -940,7 +971,7 @@ Connection::stepStatement(sqlite3_stmt *aStatement)
}
}
srv = WaitForUnlockNotify(mDBConn);
srv = WaitForUnlockNotify(aNativeConnection);
if (srv != SQLITE_OK) {
break;
}
@ -959,21 +990,26 @@ Connection::stepStatement(sqlite3_stmt *aStatement)
duration.ToMilliseconds());
}
(void)::sqlite3_extended_result_codes(mDBConn, 0);
(void)::sqlite3_extended_result_codes(aNativeConnection, 0);
// Drop off the extended result bits of the result code.
return srv & 0xFF;
}
int
Connection::prepareStatement(const nsCString &aSQL,
Connection::prepareStatement(sqlite3 *aNativeConnection, const nsCString &aSQL,
sqlite3_stmt **_stmt)
{
// We should not even try to prepare statements after the connection has
// been closed.
if (isClosed())
return SQLITE_MISUSE;
bool checkedMainThread = false;
(void)::sqlite3_extended_result_codes(mDBConn, 1);
(void)::sqlite3_extended_result_codes(aNativeConnection, 1);
int srv;
while((srv = ::sqlite3_prepare_v2(mDBConn,
while((srv = ::sqlite3_prepare_v2(aNativeConnection,
aSQL.get(),
-1,
_stmt,
@ -986,7 +1022,7 @@ Connection::prepareStatement(const nsCString &aSQL,
}
}
srv = WaitForUnlockNotify(mDBConn);
srv = WaitForUnlockNotify(aNativeConnection);
if (srv != SQLITE_OK) {
break;
}
@ -997,7 +1033,7 @@ Connection::prepareStatement(const nsCString &aSQL,
warnMsg.AppendLiteral("The SQL statement '");
warnMsg.Append(aSQL);
warnMsg.AppendLiteral("' could not be compiled due to an error: ");
warnMsg.Append(::sqlite3_errmsg(mDBConn));
warnMsg.Append(::sqlite3_errmsg(aNativeConnection));
#ifdef DEBUG
NS_WARNING(warnMsg.get());
@ -1005,7 +1041,7 @@ Connection::prepareStatement(const nsCString &aSQL,
PR_LOG(gStorageLog, PR_LOG_ERROR, ("%s", warnMsg.get()));
}
(void)::sqlite3_extended_result_codes(mDBConn, 0);
(void)::sqlite3_extended_result_codes(aNativeConnection, 0);
// Drop off the extended result bits of the result code.
int rc = srv & 0xFF;
// sqlite will return OK on a comment only string and set _stmt to nullptr.
@ -1020,13 +1056,14 @@ Connection::prepareStatement(const nsCString &aSQL,
int
Connection::executeSql(const char *aSqlString)
Connection::executeSql(sqlite3 *aNativeConnection, const char *aSqlString)
{
if (!mDBConn)
if (isClosed())
return SQLITE_MISUSE;
TimeStamp startTime = TimeStamp::Now();
int srv = ::sqlite3_exec(mDBConn, aSqlString, nullptr, nullptr, nullptr);
int srv = ::sqlite3_exec(aNativeConnection, aSqlString, nullptr, nullptr,
nullptr);
// Report very slow SQL statements to Telemetry
TimeDuration duration = TimeStamp::Now() - startTime;
@ -1076,10 +1113,13 @@ Connection::Close()
NS_ENSURE_TRUE(asyncCloseWasCalled, NS_ERROR_UNEXPECTED);
}
// setClosedState nullifies our connection pointer, so we take a raw pointer
// off it, to pass it through the close procedure.
sqlite3 *nativeConn = mDBConn;
nsresult rv = setClosedState();
NS_ENSURE_SUCCESS(rv, rv);
return internalClose();
return internalClose(nativeConn);
}
NS_IMETHODIMP
@ -1094,6 +1134,9 @@ Connection::AsyncClose(mozIStorageCompletionCallback *aCallback)
nsIEventTarget *asyncThread = getAsyncExecutionTarget();
NS_ENSURE_TRUE(asyncThread, NS_ERROR_NOT_INITIALIZED);
// setClosedState nullifies our connection pointer, so we take a raw pointer
// off it, to pass it through the close procedure.
sqlite3 *nativeConn = mDBConn;
nsresult rv = setClosedState();
NS_ENSURE_SUCCESS(rv, rv);
@ -1109,6 +1152,7 @@ Connection::AsyncClose(mozIStorageCompletionCallback *aCallback)
// We need to lock because we're modifying mAsyncExecutionThread
MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
closeEvent = new AsyncCloseConnection(this,
nativeConn,
completeEvent,
mAsyncExecutionThread.forget());
}
@ -1240,7 +1284,7 @@ Connection::GetDefaultPageSize(int32_t *_defaultPageSize)
NS_IMETHODIMP
Connection::GetConnectionReady(bool *_ready)
{
*_ready = ConnectionReady();
*_ready = connectionReady();
return NS_OK;
}
@ -1335,7 +1379,7 @@ Connection::CreateStatement(const nsACString &aSQLStatement,
nsRefPtr<Statement> statement(new Statement());
NS_ENSURE_TRUE(statement, NS_ERROR_OUT_OF_MEMORY);
nsresult rv = statement->initialize(this, aSQLStatement);
nsresult rv = statement->initialize(this, mDBConn, aSQLStatement);
NS_ENSURE_SUCCESS(rv, rv);
Statement *rawPtr;
@ -1354,7 +1398,7 @@ Connection::CreateAsyncStatement(const nsACString &aSQLStatement,
nsRefPtr<AsyncStatement> statement(new AsyncStatement());
NS_ENSURE_TRUE(statement, NS_ERROR_OUT_OF_MEMORY);
nsresult rv = statement->initialize(this, aSQLStatement);
nsresult rv = statement->initialize(this, mDBConn, aSQLStatement);
NS_ENSURE_SUCCESS(rv, rv);
AsyncStatement *rawPtr;
@ -1368,7 +1412,7 @@ Connection::ExecuteSimpleSQL(const nsACString &aSQLStatement)
{
if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
int srv = executeSql(PromiseFlatCString(aSQLStatement).get());
int srv = executeSql(mDBConn, PromiseFlatCString(aSQLStatement).get());
return convertResultCode(srv);
}
@ -1396,7 +1440,8 @@ Connection::ExecuteAsync(mozIStorageBaseStatement **aStatements,
}
// Dispatch to the background
return AsyncExecuteStatements::execute(stmts, this, aCallback, _handle);
return AsyncExecuteStatements::execute(stmts, this, mDBConn, aCallback,
_handle);
}
NS_IMETHODIMP
@ -1459,19 +1504,26 @@ Connection::BeginTransactionAs(int32_t aTransactionType)
{
if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
return beginTransactionInternal(mDBConn, aTransactionType);
}
nsresult
Connection::beginTransactionInternal(sqlite3 *aNativeConnection,
int32_t aTransactionType)
{
SQLiteMutexAutoLock lockedScope(sharedDBMutex);
if (mTransactionInProgress)
return NS_ERROR_FAILURE;
nsresult rv;
switch(aTransactionType) {
case TRANSACTION_DEFERRED:
rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("BEGIN DEFERRED"));
rv = convertResultCode(executeSql(aNativeConnection, "BEGIN DEFERRED"));
break;
case TRANSACTION_IMMEDIATE:
rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("BEGIN IMMEDIATE"));
rv = convertResultCode(executeSql(aNativeConnection, "BEGIN IMMEDIATE"));
break;
case TRANSACTION_EXCLUSIVE:
rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("BEGIN EXCLUSIVE"));
rv = convertResultCode(executeSql(aNativeConnection, "BEGIN EXCLUSIVE"));
break;
default:
return NS_ERROR_ILLEGAL_VALUE;
@ -1487,11 +1539,17 @@ Connection::CommitTransaction()
if (!mDBConn)
return NS_ERROR_NOT_INITIALIZED;
return commitTransactionInternal(mDBConn);
}
nsresult
Connection::commitTransactionInternal(sqlite3 *aNativeConnection)
{
SQLiteMutexAutoLock lockedScope(sharedDBMutex);
if (!mTransactionInProgress)
return NS_ERROR_UNEXPECTED;
nsresult rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("COMMIT TRANSACTION"));
nsresult rv =
convertResultCode(executeSql(aNativeConnection, "COMMIT TRANSACTION"));
if (NS_SUCCEEDED(rv))
mTransactionInProgress = false;
return rv;
@ -1503,11 +1561,18 @@ Connection::RollbackTransaction()
if (!mDBConn)
return NS_ERROR_NOT_INITIALIZED;
return rollbackTransactionInternal(mDBConn);
}
nsresult
Connection::rollbackTransactionInternal(sqlite3 *aNativeConnection)
{
SQLiteMutexAutoLock lockedScope(sharedDBMutex);
if (!mTransactionInProgress)
return NS_ERROR_UNEXPECTED;
nsresult rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("ROLLBACK TRANSACTION"));
nsresult rv =
convertResultCode(executeSql(aNativeConnection, "ROLLBACK TRANSACTION"));
if (NS_SUCCEEDED(rv))
mTransactionInProgress = false;
return rv;
@ -1523,7 +1588,7 @@ Connection::CreateTable(const char *aTableName,
if (!buf)
return NS_ERROR_OUT_OF_MEMORY;
int srv = executeSql(buf);
int srv = executeSql(mDBConn, buf);
::PR_smprintf_free(buf);
return convertResultCode(srv);

View File

@ -96,9 +96,29 @@ public:
*/
nsresult initialize(nsIFileURL *aFileURL);
// fetch the native handle
sqlite3 *GetNativeConnection() { return mDBConn; }
operator sqlite3 *() const { return mDBConn; }
/**
* Fetches runtime status information for this connection.
*
* @param aStatusOption One of the SQLITE_DBSTATUS options defined at
* http://www.sqlite.org/c3ref/c_dbstatus_options.html
* @param [optional] aMaxValue if provided, will be set to the highest
* istantaneous value.
* @return the current value for the specified option.
*/
int32_t getSqliteRuntimeStatus(int32_t aStatusOption,
int32_t* aMaxValue=nullptr);
/**
* Registers/unregisters a commit hook callback.
*
* @param aCallbackFn a callback function to be invoked on transactions
* commit. Pass nullptr to unregister the current callback.
* @param [optional] aData if provided, will be passed to the callback.
* @see http://sqlite.org/c3ref/commit_hook.html
*/
void setCommitHook(int (*aCallbackFn)(void *) , void *aData=nullptr) {
MOZ_ASSERT(mDBConn, "A connection must exist at this point");
::sqlite3_commit_hook(mDBConn, aCallbackFn, aData);
};
/**
* Lazily creates and returns a background execution thread. In the future,
@ -112,8 +132,12 @@ public:
/**
* Mutex used by asynchronous statements to protect state. The mutex is
* declared on the connection object because there is no contention between
* asynchronous statements (they are serialized on mAsyncExecutionThread). It
* also protects mPendingStatements.
* asynchronous statements (they are serialized on mAsyncExecutionThread).
* Currently protects:
* - Connection.mAsyncExecutionThreadShuttingDown
* - Connection.mAsyncExecutionThread
* - Connection.mConnectionClosed
* - AsyncExecuteStatements.mCancelRequested
*/
Mutex sharedAsyncExecutionMutex;
@ -134,7 +158,7 @@ public:
/**
* Closes the SQLite database, and warns about any non-finalized statements.
*/
nsresult internalClose();
nsresult internalClose(sqlite3 *aDBConn);
/**
* Obtains the filename of the connection. Useful for logging.
@ -144,39 +168,52 @@ public:
/**
* Creates an sqlite3 prepared statement object from an SQL string.
*
* @param aNativeConnection
* The underlying Sqlite connection to prepare the statement with.
* @param aSQL
* The SQL statement string to compile.
* @param _stmt
* New sqlite3_stmt object.
* @return the result from sqlite3_prepare_v2.
*/
int prepareStatement(const nsCString &aSQL, sqlite3_stmt **_stmt);
int prepareStatement(sqlite3* aNativeConnection,
const nsCString &aSQL, sqlite3_stmt **_stmt);
/**
* Performs a sqlite3_step on aStatement, while properly handling SQLITE_LOCKED
* when not on the main thread by waiting until we are notified.
*
* @param aNativeConnection
* The underlying Sqlite connection to step the statement with.
* @param aStatement
* A pointer to a sqlite3_stmt object.
* @return the result from sqlite3_step.
*/
int stepStatement(sqlite3_stmt* aStatement);
bool ConnectionReady() {
return mDBConn != nullptr;
}
int stepStatement(sqlite3* aNativeConnection, sqlite3_stmt* aStatement);
/**
* True if this connection is currently shutting down.
* Raw connection transaction management.
*
* In particular, if |isClosing(true)| returns |true|, any sqlite3 statement
* belonging to this connection must be discarded as its memory has already
* been released to sqlite3.
*
* @param aResultOnceClosed
* The value to return if closing has completed.
* @see BeginTransactionAs, CommitTransaction, RollbackTransaction.
*/
bool isClosing(bool aResultOnceClosed = false);
nsresult beginTransactionInternal(sqlite3 *aNativeConnection,
int32_t aTransactionType=TRANSACTION_DEFERRED);
nsresult commitTransactionInternal(sqlite3 *aNativeConnection);
nsresult rollbackTransactionInternal(sqlite3 *aNativeConnection);
bool connectionReady();
/**
* True if this connection is shutting down but not yet closed.
*/
bool isClosing();
/**
* True if the underlying connection is closed.
* Any sqlite resources may be lost when this returns true, so nothing should
* try to use them.
*/
bool isClosed();
nsresult initializeClone(Connection *aClone, bool aReadOnly);
@ -195,11 +232,13 @@ private:
/**
* Helper for calls to sqlite3_exec. Reports long delays to Telemetry.
*
* @param aNativeConnection
* The underlying Sqlite connection to execute the query with.
* @param aSqlString
* SQL string to execute
* @return the result from sqlite3_exec.
*/
int executeSql(const char *aSqlString);
int executeSql(sqlite3 *aNativeConnection, const char *aSqlString);
/**
* Describes a certain primitive type in the database.
@ -256,25 +295,34 @@ private:
* returns null.
*
* This variable should be accessed while holding the
* mAsyncExecutionMutex.
* sharedAsyncExecutionMutex.
*/
bool mAsyncExecutionThreadShuttingDown;
/**
* Set to true just prior to calling sqlite3_close on the
* connection.
*
* This variable should be accessed while holding the
* sharedAsyncExecutionMutex.
*/
bool mConnectionClosed;
/**
* Tracks if we have a transaction in progress or not. Access protected by
* mDBMutex.
* sharedDBMutex.
*/
bool mTransactionInProgress;
/**
* Stores the mapping of a given function by name to its instance. Access is
* protected by mDBMutex.
* protected by sharedDBMutex.
*/
nsDataHashtable<nsCStringHashKey, FunctionInfo> mFunctions;
/**
* Stores the registered progress handler for the database connection. Access
* is protected by mDBMutex.
* is protected by sharedDBMutex.
*/
nsCOMPtr<mozIStorageProgressHandler> mProgressHandler;

View File

@ -94,28 +94,24 @@ StorageSQLiteDistinguishedAmount()
nsresult
ReportConn(nsIHandleReportCallback *aHandleReport,
nsISupports *aData,
sqlite3 *aConn,
Connection *aConn,
const nsACString &aPathHead,
const nsACString &aKind,
const nsACString &aDesc,
int aOption,
int32_t aOption,
size_t *aTotal)
{
nsCString path(aPathHead);
path.Append(aKind);
path.AppendLiteral("-used");
int curr = 0, max = 0;
int rc = ::sqlite3_db_status(aConn, aOption, &curr, &max, 0);
nsresult rv = convertResultCode(rc);
int32_t val = aConn->getSqliteRuntimeStatus(aOption);
nsresult rv = aHandleReport->Callback(EmptyCString(), path,
nsIMemoryReporter::KIND_HEAP,
nsIMemoryReporter::UNITS_BYTES,
int64_t(val), aDesc, aData);
NS_ENSURE_SUCCESS(rv, rv);
rv = aHandleReport->Callback(EmptyCString(), path,
nsIMemoryReporter::KIND_HEAP,
nsIMemoryReporter::UNITS_BYTES, int64_t(curr),
aDesc, aData);
NS_ENSURE_SUCCESS(rv, rv);
*aTotal += curr;
*aTotal += val;
return NS_OK;
}
@ -155,7 +151,7 @@ Service::CollectReports(nsIHandleReportCallback *aHandleReport,
NS_NAMED_LITERAL_CSTRING(stmtDesc,
"Memory (approximate) used by all prepared statements used by "
"connections to this database.");
rv = ReportConn(aHandleReport, aData, *conn.get(), pathHead,
rv = ReportConn(aHandleReport, aData, conn, pathHead,
NS_LITERAL_CSTRING("stmt"), stmtDesc,
SQLITE_DBSTATUS_STMT_USED, &totalConnSize);
NS_ENSURE_SUCCESS(rv, rv);
@ -163,7 +159,7 @@ Service::CollectReports(nsIHandleReportCallback *aHandleReport,
NS_NAMED_LITERAL_CSTRING(cacheDesc,
"Memory (approximate) used by all pager caches used by connections "
"to this database.");
rv = ReportConn(aHandleReport, aData, *conn.get(), pathHead,
rv = ReportConn(aHandleReport, aData, conn, pathHead,
NS_LITERAL_CSTRING("cache"), cacheDesc,
SQLITE_DBSTATUS_CACHE_USED, &totalConnSize);
NS_ENSURE_SUCCESS(rv, rv);
@ -171,7 +167,7 @@ Service::CollectReports(nsIHandleReportCallback *aHandleReport,
NS_NAMED_LITERAL_CSTRING(schemaDesc,
"Memory (approximate) used to store the schema for all databases "
"associated with connections to this database.");
rv = ReportConn(aHandleReport, aData, *conn.get(), pathHead,
rv = ReportConn(aHandleReport, aData, conn, pathHead,
NS_LITERAL_CSTRING("schema"), schemaDesc,
SQLITE_DBSTATUS_SCHEMA_USED, &totalConnSize);
NS_ENSURE_SUCCESS(rv, rv);
@ -350,7 +346,7 @@ Service::minimizeMemory()
for (uint32_t i = 0; i < connections.Length(); i++) {
nsRefPtr<Connection> conn = connections[i];
if (conn->ConnectionReady()) {
if (conn->connectionReady()) {
NS_NAMED_LITERAL_CSTRING(shrinkPragma, "PRAGMA shrink_memory");
nsCOMPtr<mozIStorageConnection> syncConn = do_QueryInterface(
NS_ISUPPORTS_CAST(mozIStorageAsyncConnection*, conn));
@ -918,9 +914,6 @@ Service::Observe(nsISupports *, const char *aTopic, const char16_t *)
anyOpen = false;
for (uint32_t i = 0; i < connections.Length(); i++) {
nsRefPtr<Connection> &conn = connections[i];
// While it would be nice to close all connections, we only
// check async ones for now.
if (conn->isClosing()) {
anyOpen = true;
break;
@ -936,7 +929,7 @@ Service::Observe(nsISupports *, const char *aTopic, const char16_t *)
nsTArray<nsRefPtr<Connection> > connections;
getConnections(connections);
for (uint32_t i = 0, n = connections.Length(); i < n; i++) {
if (connections[i]->ConnectionReady()) {
if (!connections[i]->isClosed()) {
MOZ_CRASH();
}
}

View File

@ -135,20 +135,21 @@ Statement::Statement()
nsresult
Statement::initialize(Connection *aDBConnection,
sqlite3 *aNativeConnection,
const nsACString &aSQLStatement)
{
NS_ASSERTION(aDBConnection, "No database connection given!");
NS_ASSERTION(!mDBStatement, "Statement already initialized!");
MOZ_ASSERT(aDBConnection, "No database connection given!");
MOZ_ASSERT(!aDBConnection->isClosed(), "Database connection should be valid");
MOZ_ASSERT(!mDBStatement, "Statement already initialized!");
MOZ_ASSERT(aNativeConnection, "No native connection given!");
DebugOnly<sqlite3 *> db = aDBConnection->GetNativeConnection();
NS_ASSERTION(db, "We should never be called with a null sqlite3 database!");
int srv = aDBConnection->prepareStatement(PromiseFlatCString(aSQLStatement),
int srv = aDBConnection->prepareStatement(aNativeConnection,
PromiseFlatCString(aSQLStatement),
&mDBStatement);
if (srv != SQLITE_OK) {
PR_LOG(gStorageLog, PR_LOG_ERROR,
("Sqlite statement prepare error: %d '%s'", srv,
::sqlite3_errmsg(db)));
::sqlite3_errmsg(aNativeConnection)));
PR_LOG(gStorageLog, PR_LOG_ERROR,
("Statement was: '%s'", PromiseFlatCString(aSQLStatement).get()));
return NS_ERROR_FAILURE;
@ -159,6 +160,7 @@ Statement::initialize(Connection *aDBConnection,
mDBStatement));
mDBConnection = aDBConnection;
mNativeConnection = aNativeConnection;
mParamCount = ::sqlite3_bind_parameter_count(mDBStatement);
mResultColumnCount = ::sqlite3_column_count(mDBStatement);
mColumnNames.Clear();
@ -284,7 +286,8 @@ Statement::getAsyncStatement(sqlite3_stmt **_stmt)
// If we do not yet have a cached async statement, clone our statement now.
if (!mAsyncStatement) {
nsDependentCString sql(::sqlite3_sql(mDBStatement));
int rc = mDBConnection->prepareStatement(sql, &mAsyncStatement);
int rc = mDBConnection->prepareStatement(mNativeConnection, sql,
&mAsyncStatement);
if (rc != SQLITE_OK) {
*_stmt = nullptr;
return rc;
@ -335,7 +338,7 @@ Statement::Clone(mozIStorageStatement **_statement)
NS_ENSURE_TRUE(statement, NS_ERROR_OUT_OF_MEMORY);
nsAutoCString sql(::sqlite3_sql(mDBStatement));
nsresult rv = statement->initialize(mDBConnection, sql);
nsresult rv = statement->initialize(mDBConnection, mNativeConnection, sql);
NS_ENSURE_SUCCESS(rv, rv);
statement.forget(_statement);
@ -356,7 +359,7 @@ Statement::internalFinalize(bool aDestructing)
int srv = SQLITE_OK;
if (!mDBConnection->isClosing(true)) {
if (!mDBConnection->isClosed()) {
//
// The connection is still open. While statement finalization and
// closing may, in some cases, take place in two distinct threads,
@ -632,7 +635,7 @@ Statement::ExecuteStep(bool *_moreResults)
// We have bound, so now we can clear our array.
mParamsArray = nullptr;
}
int srv = mDBConnection->stepStatement(mDBStatement);
int srv = mDBConnection->stepStatement(mNativeConnection, mDBStatement);
#ifdef PR_LOGGING
if (srv != SQLITE_ROW && srv != SQLITE_DONE) {

View File

@ -47,10 +47,13 @@ public:
*
* @param aDBConnection
* The Connection object this statement is associated with.
* @param aNativeConnection
* The native Sqlite connection this statement is associated with.
* @param aSQLStatement
* The SQL statement to prepare that this object will represent.
*/
nsresult initialize(Connection *aDBConnection,
sqlite3* aNativeConnection,
const nsACString &aSQLStatement);

View File

@ -71,15 +71,6 @@ public:
operator BindingParamsArray *() const { return mParamsArray; }
/**
* Provide the ability to coerce back to a sqlite3 * connection for purposes
* of getting an error message out of it.
*/
operator sqlite3 *() const
{
return mStatementOwner->getOwner()->GetNativeConnection();
}
/**
* NULLs out our sqlite3_stmt (it is held by the owner) after reseting it and
* clear all bindings to it. This is expected to occur on the async thread.

View File

@ -48,7 +48,7 @@ check_transaction(mozIStorageConnection *aDB,
{
// -- install a transaction commit hook.
int commit = 0;
::sqlite3_commit_hook(*static_cast<Connection *>(aDB), commit_hook, &commit);
static_cast<Connection *>(aDB)->setCommitHook(commit_hook, &commit);
nsRefPtr<AsyncStatementSpinner> asyncSpin(new AsyncStatementSpinner());
nsCOMPtr<mozIStoragePendingStatement> asyncPend;
@ -60,7 +60,7 @@ check_transaction(mozIStorageConnection *aDB,
asyncSpin->SpinUntilCompleted();
// -- uninstall the transaction commit hook.
::sqlite3_commit_hook(*static_cast<Connection *>(aDB), nullptr, nullptr);
static_cast<Connection *>(aDB)->setCommitHook(nullptr);
// -- check transaction
do_check_eq(aTransactionExpected, !!commit);

View File

@ -35,10 +35,6 @@ Cu.import("resource://gre/modules/NetUtil.jsm");
Services.prefs.setBoolPref("marionette.contentListener", false);
let appName = Services.appinfo.name;
// dumpn needed/used by dbg-transport.js
this.dumpn = function dumpn(str) {
logger.trace(str);
}
let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
let DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils.js");
this.DevToolsUtils = DevToolsUtils;

View File

@ -215,18 +215,17 @@ SetJournalMode(nsCOMPtr<mozIStorageConnection>& aDBConn,
return JOURNAL_DELETE;
}
class BlockingConnectionCloseCallback MOZ_FINAL : public mozIStorageCompletionCallback {
class ConnectionCloseCallback MOZ_FINAL : public mozIStorageCompletionCallback {
bool mDone;
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_MOZISTORAGECOMPLETIONCALLBACK
BlockingConnectionCloseCallback();
void Spin();
ConnectionCloseCallback();
};
NS_IMETHODIMP
BlockingConnectionCloseCallback::Complete(nsresult, nsISupports*)
ConnectionCloseCallback::Complete(nsresult, nsISupports*)
{
mDone = true;
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
@ -240,21 +239,14 @@ BlockingConnectionCloseCallback::Complete(nsresult, nsISupports*)
return NS_OK;
}
BlockingConnectionCloseCallback::BlockingConnectionCloseCallback()
ConnectionCloseCallback::ConnectionCloseCallback()
: mDone(false)
{
MOZ_ASSERT(NS_IsMainThread());
}
void BlockingConnectionCloseCallback::Spin() {
nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
while (!mDone) {
NS_ProcessNextEvent(thread);
}
}
NS_IMPL_ISUPPORTS1(
BlockingConnectionCloseCallback
ConnectionCloseCallback
, mozIStorageCompletionCallback
)
@ -1939,12 +1931,11 @@ Database::Shutdown()
);
DispatchToAsyncThread(event);
nsRefPtr<BlockingConnectionCloseCallback> closeListener =
new BlockingConnectionCloseCallback();
(void)mMainConn->AsyncClose(closeListener);
closeListener->Spin();
mClosed = true;
nsRefPtr<ConnectionCloseCallback> closeListener =
new ConnectionCloseCallback();
(void)mMainConn->AsyncClose(closeListener);
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -10,7 +10,7 @@
// on the main thread, and false if we are running on a worker thread.
var { Ci, Cu } = require("chrome");
var Services = require("Services");
var setTimeout = Cu.import("resource://gre/modules/Timer.jsm", {}).setTimeout;
var { setTimeout } = require("Timer");
/**
* Turn the error |aError| into a string, without fail.

View File

@ -23,6 +23,7 @@ let Debugger = sandbox.Debugger;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
let Timer = Cu.import("resource://gre/modules/Timer.jsm", {});
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", "resource://gre/modules/FileUtils.jsm");
@ -63,6 +64,7 @@ BuiltinProvider.prototype = {
modules: {
"Debugger": Debugger,
"Services": Object.create(Services),
"Timer": Object.create(Timer),
"toolkit/loader": loader,
"source-map": SourceMap,
},
@ -141,6 +143,7 @@ SrcdirProvider.prototype = {
modules: {
"Debugger": Debugger,
"Services": Object.create(Services),
"Timer": Object.create(Timer),
"toolkit/loader": loader,
"source-map": SourceMap,
},

View File

@ -0,0 +1,168 @@
/* -*- tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
/**
* Methods shared between RootActor and BrowserTabActor.
*/
/**
* Populate |this._extraActors| as specified by |aFactories|, reusing whatever
* actors are already there. Add all actors in the final extra actors table to
* |aPool|.
*
* The root actor and the tab actor use this to instantiate actors that other
* parts of the browser have specified with DebuggerServer.addTabActor antd
* DebuggerServer.addGlobalActor.
*
* @param aFactories
* An object whose own property names are the names of properties to add to
* some reply packet (say, a tab actor grip or the "listTabs" response
* form), and whose own property values are actor constructor functions, as
* documented for addTabActor and addGlobalActor.
*
* @param this
* The BrowserRootActor or BrowserTabActor with which the new actors will
* be associated. It should support whatever API the |aFactories|
* constructor functions might be interested in, as it is passed to them.
* For the sake of CommonCreateExtraActors itself, it should have at least
* the following properties:
*
* - _extraActors
* An object whose own property names are factory table (and packet)
* property names, and whose values are no-argument actor constructors,
* of the sort that one can add to an ActorPool.
*
* - conn
* The DebuggerServerConnection in which the new actors will participate.
*
* - actorID
* The actor's name, for use as the new actors' parentID.
*/
exports.createExtraActors = function createExtraActors(aFactories, aPool) {
// Walk over global actors added by extensions.
for (let name in aFactories) {
let actor = this._extraActors[name];
if (!actor) {
actor = aFactories[name].bind(null, this.conn, this);
actor.prototype = aFactories[name].prototype;
actor.parentID = this.actorID;
this._extraActors[name] = actor;
}
aPool.addActor(actor);
}
}
/**
* Append the extra actors in |this._extraActors|, constructed by a prior call
* to CommonCreateExtraActors, to |aObject|.
*
* @param aObject
* The object to which the extra actors should be added, under the
* property names given in the |aFactories| table passed to
* CommonCreateExtraActors.
*
* @param this
* The BrowserRootActor or BrowserTabActor whose |_extraActors| table we
* should use; see above.
*/
exports.appendExtraActors = function appendExtraActors(aObject) {
for (let name in this._extraActors) {
let actor = this._extraActors[name];
aObject[name] = actor.actorID;
}
}
/**
* Construct an ActorPool.
*
* ActorPools are actorID -> actor mapping and storage. These are
* used to accumulate and quickly dispose of groups of actors that
* share a lifetime.
*/
function ActorPool(aConnection)
{
this.conn = aConnection;
this._cleanups = {};
this._actors = {};
}
ActorPool.prototype = {
/**
* Add an actor to the actor pool. If the actor doesn't have an ID,
* allocate one from the connection.
*
* @param aActor object
* The actor implementation. If the object has a
* 'disconnect' property, it will be called when the actor
* pool is cleaned up.
*/
addActor: function AP_addActor(aActor) {
aActor.conn = this.conn;
if (!aActor.actorID) {
let prefix = aActor.actorPrefix;
if (typeof aActor == "function") {
// typeName is a convention used with protocol.js-based actors
prefix = aActor.prototype.actorPrefix || aActor.prototype.typeName;
}
aActor.actorID = this.conn.allocID(prefix || undefined);
}
if (aActor.registeredPool) {
aActor.registeredPool.removeActor(aActor);
}
aActor.registeredPool = this;
this._actors[aActor.actorID] = aActor;
if (aActor.disconnect) {
this._cleanups[aActor.actorID] = aActor;
}
},
get: function AP_get(aActorID) {
return this._actors[aActorID];
},
has: function AP_has(aActorID) {
return aActorID in this._actors;
},
/**
* Returns true if the pool is empty.
*/
isEmpty: function AP_isEmpty() {
return Object.keys(this._actors).length == 0;
},
/**
* Remove an actor from the actor pool.
*/
removeActor: function AP_remove(aActor) {
delete this._actors[aActor.actorID];
delete this._cleanups[aActor.actorID];
},
/**
* Match the api expected by the protocol library.
*/
unmanage: function(aActor) {
return this.removeActor(aActor);
},
/**
* Run all actor cleanups.
*/
cleanup: function AP_cleanup() {
for each (let actor in this._cleanups) {
actor.disconnect();
}
this._cleanups = {};
}
}
exports.ActorPool = ActorPool;

View File

@ -8,9 +8,9 @@ const protocol = require("devtools/server/protocol");
const {method, RetVal} = protocol;
const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
const {LongStringActor} = require("devtools/server/actors/string");
const {DebuggerServer} = require("devtools/server/main");
Cu.import("resource://gre/modules/PermissionsTable.jsm")
Cu.import('resource://gre/modules/devtools/dbg-server.jsm');
const APP_MAP = {
'{ec8030f7-c20a-464f-9b0e-13a3a9e97384}': 'firefox',

View File

@ -8,7 +8,6 @@ const {Arg, method, RetVal} = protocol;
const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
Cu.import("resource://gre/modules/Services.jsm");
Cu.import('resource://gre/modules/devtools/dbg-server.jsm');
exports.register = function(handle) {
handle.addGlobalActor(PreferenceActor, "preferenceActor");

View File

@ -6,79 +6,11 @@
"use strict";
let devtools_ = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
let { createExtraActors, appendExtraActors } = devtools_.require("devtools/server/actors/common");
/* Root actor for the remote debugging protocol. */
/**
* Methods shared between RootActor and BrowserTabActor.
*/
/**
* Populate |this._extraActors| as specified by |aFactories|, reusing whatever
* actors are already there. Add all actors in the final extra actors table to
* |aPool|.
*
* The root actor and the tab actor use this to instantiate actors that other
* parts of the browser have specified with DebuggerServer.addTabActor antd
* DebuggerServer.addGlobalActor.
*
* @param aFactories
* An object whose own property names are the names of properties to add to
* some reply packet (say, a tab actor grip or the "listTabs" response
* form), and whose own property values are actor constructor functions, as
* documented for addTabActor and addGlobalActor.
*
* @param this
* The BrowserRootActor or BrowserTabActor with which the new actors will
* be associated. It should support whatever API the |aFactories|
* constructor functions might be interested in, as it is passed to them.
* For the sake of CommonCreateExtraActors itself, it should have at least
* the following properties:
*
* - _extraActors
* An object whose own property names are factory table (and packet)
* property names, and whose values are no-argument actor constructors,
* of the sort that one can add to an ActorPool.
*
* - conn
* The DebuggerServerConnection in which the new actors will participate.
*
* - actorID
* The actor's name, for use as the new actors' parentID.
*/
function CommonCreateExtraActors(aFactories, aPool) {
// Walk over global actors added by extensions.
for (let name in aFactories) {
let actor = this._extraActors[name];
if (!actor) {
actor = aFactories[name].bind(null, this.conn, this);
actor.prototype = aFactories[name].prototype;
actor.parentID = this.actorID;
this._extraActors[name] = actor;
}
aPool.addActor(actor);
}
}
/**
* Append the extra actors in |this._extraActors|, constructed by a prior call
* to CommonCreateExtraActors, to |aObject|.
*
* @param aObject
* The object to which the extra actors should be added, under the
* property names given in the |aFactories| table passed to
* CommonCreateExtraActors.
*
* @param this
* The BrowserRootActor or BrowserTabActor whose |_extraActors| table we
* should use; see above.
*/
function CommonAppendExtraActors(aObject) {
for (let name in this._extraActors) {
let actor = this._extraActors[name];
aObject[name] = actor.actorID;
}
}
/**
* Create a remote debugging protocol root actor.
*
@ -364,8 +296,8 @@ RootActor.prototype = {
},
/* Support for DebuggerServer.addGlobalActor. */
_createExtraActors: CommonCreateExtraActors,
_appendExtraActors: CommonAppendExtraActors,
_createExtraActors: createExtraActors,
_appendExtraActors: appendExtraActors,
/* ThreadActor hooks. */

View File

@ -3,6 +3,7 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
let B2G_ID = "{3c2e2abc-06d4-11e1-ac3b-374f68613e61}";
@ -554,7 +555,7 @@ ThreadActor.prototype = {
this._prettyPrintWorker.addEventListener(
"error", this._onPrettyPrintError, false);
if (wantLogging) {
if (dumpn.wantLogging) {
this._prettyPrintWorker.addEventListener("message", this._onPrettyPrintMsg, false);
const postMsg = this._prettyPrintWorker.postMessage;

View File

@ -5,7 +5,7 @@
"use strict";
let {Cu} = require("chrome");
let {DebuggerServer} = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
let {DebuggerServer} = require("devtools/server/main");
let {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
let {Class} = require("sdk/core/heritage");

View File

@ -5,7 +5,7 @@
"use strict";
const { Cu } = require("chrome");
const { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
const { DebuggerServer } = require("devtools/server/main");
const { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
Cu.import("resource://gre/modules/jsdebugger.jsm");

View File

@ -6,8 +6,14 @@
"use strict";
let {Cu} = require("chrome");
let {Ci,Cu} = require("chrome");
let {createExtraActors, appendExtraActors} = require("devtools/server/actors/common");
let DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
let {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
// Assumptions on events module:
@ -17,6 +23,10 @@ XPCOMUtils.defineLazyGetter(this, "events", () => {
return devtools.require("sdk/event/core");
});
// Also depends on following symbols, shared by common scope with main.js:
// DebuggerServer, CommonCreateExtraActors, CommonAppendExtraActors, ActorPool,
// ThreadActor
/**
* Browser-specific actors.
*/
@ -661,8 +671,8 @@ TabActor.prototype = {
},
/* Support for DebuggerServer.addTabActor. */
_createExtraActors: CommonCreateExtraActors,
_appendExtraActors: CommonAppendExtraActors,
_createExtraActors: createExtraActors,
_appendExtraActors: appendExtraActors,
/**
* Does the actual work of attching to a tab.
@ -1040,9 +1050,15 @@ TabActor.prototype = {
* True if the window.console object is native, or false otherwise.
*/
hasNativeConsoleAPI: function BTA_hasNativeConsoleAPI(aWindow) {
// Do not expose WebConsoleActor function directly as it is always
// loaded after the BrowserTabActor
return WebConsoleActor.prototype.hasNativeConsoleAPI(aWindow);
let isNative = false;
try {
// We are very explicitly examining the "console" property of
// the non-Xrayed object here.
let console = aWindow.wrappedJSObject.console;
isNative = console instanceof aWindow.Console;
}
catch (ex) { }
return isNative;
}
};

View File

@ -6,28 +6,34 @@
"use strict";
let Cc = Components.classes;
let Ci = Components.interfaces;
let Cu = Components.utils;
let {Cc, Ci, Cu} = require("chrome");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
let devtools = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
let { DebuggerServer, ActorPool } = require("devtools/server/main");
// Symbols from script.js
let { ThreadActor, EnvironmentActor, ObjectActor, LongStringActor } = DebuggerServer;
Cu.import("resource://gre/modules/jsdebugger.jsm");
addDebuggerToGlobal(this);
XPCOMUtils.defineLazyModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyGetter(this, "NetworkMonitor", () => {
return devtools.require("devtools/toolkit/webconsole/network-monitor")
return require("devtools/toolkit/webconsole/network-monitor")
.NetworkMonitor;
});
XPCOMUtils.defineLazyGetter(this, "NetworkMonitorChild", () => {
return devtools.require("devtools/toolkit/webconsole/network-monitor")
return require("devtools/toolkit/webconsole/network-monitor")
.NetworkMonitorChild;
});
XPCOMUtils.defineLazyGetter(this, "ConsoleProgressListener", () => {
return devtools.require("devtools/toolkit/webconsole/network-monitor")
return require("devtools/toolkit/webconsole/network-monitor")
.ConsoleProgressListener;
});
XPCOMUtils.defineLazyGetter(this, "events", () => {
return require("sdk/event/core");
});
for (let name of ["WebConsoleUtils", "ConsoleServiceListener",
"ConsoleAPIListener", "JSTermHelpers", "JSPropertyProvider",
@ -37,7 +43,7 @@ for (let name of ["WebConsoleUtils", "ConsoleServiceListener",
if (prop == "WebConsoleUtils") {
prop = "Utils";
}
return devtools.require("devtools/toolkit/webconsole/utils")[prop];
return require("devtools/toolkit/webconsole/utils")[prop];
}.bind(null, name),
configurable: true,
enumerable: true
@ -1831,6 +1837,12 @@ NetworkEventActor.prototype.requestTypes =
"getEventTimings": NetworkEventActor.prototype.onGetEventTimings,
};
DebuggerServer.addTabActor(WebConsoleActor, "consoleActor");
DebuggerServer.addGlobalActor(WebConsoleActor, "consoleActor");
exports.register = function(handle) {
handle.addGlobalActor(WebConsoleActor, "consoleActor");
handle.addTabActor(WebConsoleActor, "consoleActor");
};
exports.unregister = function(handle) {
handle.removeGlobalActor(WebConsoleActor, "consoleActor");
handle.removeTabActor(WebConsoleActor, "consoleActor");
};

View File

@ -12,7 +12,9 @@
let { Ci, Cc, CC, Cu, Cr } = require("chrome");
let Debugger = require("Debugger");
let Services = require("Services");
let DevToolsUtils = require("devtools/toolkit/DevToolsUtils.js");
let { ActorPool } = require("devtools/server/actors/common");
let { DebuggerTransport, LocalDebuggerTransport, ChildDebuggerTransport } = require("devtools/server/transport");
let DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
let { dumpn, dbg_assert } = DevToolsUtils;
let Services = require("Services");
let EventEmitter = require("devtools/toolkit/event-emitter");
@ -30,6 +32,7 @@ this.Cu = Cu;
this.Cr = Cr;
this.Debugger = Debugger;
this.Services = Services;
this.ActorPool = ActorPool;
this.DevToolsUtils = DevToolsUtils;
this.dumpn = dumpn;
this.dbg_assert = dbg_assert;
@ -45,6 +48,7 @@ const DBG_STRINGS_URI = "chrome://global/locale/devtools/debugger.properties";
const nsFile = CC("@mozilla.org/file/local;1", "nsIFile", "initWithPath");
Cu.import("resource://gre/modules/reflect.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
dumpn.wantLogging = Services.prefs.getBoolPref("devtools.debugger.log");
Cu.import("resource://gre/modules/devtools/deprecated-sync-thenables.js");
@ -81,8 +85,6 @@ XPCOMUtils.defineLazyGetter(this, "NetworkMonitorManager", () => {
return require("devtools/toolkit/webconsole/network-monitor").NetworkMonitorManager;
});
loadSubScript.call(this, "resource://gre/modules/devtools/server/transport.js");
// XPCOM constructors
const ServerSocket = CC("@mozilla.org/network/server-socket;1",
"nsIServerSocket",
@ -359,7 +361,7 @@ var DebuggerServer = {
// In case of apps being loaded in parent process, DebuggerServer is already
// initialized and browser actors are already loaded,
// but childtab.js hasn't been loaded yet.
if (!("WebConsoleActor" in this)) {
if (!DebuggerServer.tabActorFactories.hasOwnProperty("consoleActor")) {
this.addTabActors();
}
// But webbrowser.js and childtab.js aren't loaded from shell.js.
@ -376,7 +378,7 @@ var DebuggerServer = {
*/
addTabActors: function() {
this.addActors("resource://gre/modules/devtools/server/actors/script.js");
this.addActors("resource://gre/modules/devtools/server/actors/webconsole.js");
this.registerModule("devtools/server/actors/webconsole");
this.registerModule("devtools/server/actors/inspector");
this.registerModule("devtools/server/actors/call-watcher");
this.registerModule("devtools/server/actors/canvas");
@ -822,98 +824,10 @@ if (this.exports) {
// Needed on B2G (See header note)
this.DebuggerServer = DebuggerServer;
/**
* Construct an ActorPool.
*
* ActorPools are actorID -> actor mapping and storage. These are
* used to accumulate and quickly dispose of groups of actors that
* share a lifetime.
*/
function ActorPool(aConnection)
{
this.conn = aConnection;
this._cleanups = {};
this._actors = {};
}
// Export ActorPool for requirers of main.js
if (this.exports) {
exports.ActorPool = ActorPool;
}
// Needed on B2G (See header note)
this.ActorPool = ActorPool;
ActorPool.prototype = {
/**
* Add an actor to the actor pool. If the actor doesn't have an ID,
* allocate one from the connection.
*
* @param aActor object
* The actor implementation. If the object has a
* 'disconnect' property, it will be called when the actor
* pool is cleaned up.
*/
addActor: function AP_addActor(aActor) {
aActor.conn = this.conn;
if (!aActor.actorID) {
let prefix = aActor.actorPrefix;
if (typeof aActor == "function") {
// typeName is a convention used with protocol.js-based actors
prefix = aActor.prototype.actorPrefix || aActor.prototype.typeName;
}
aActor.actorID = this.conn.allocID(prefix || undefined);
}
if (aActor.registeredPool) {
aActor.registeredPool.removeActor(aActor);
}
aActor.registeredPool = this;
this._actors[aActor.actorID] = aActor;
if (aActor.disconnect) {
this._cleanups[aActor.actorID] = aActor;
}
},
get: function AP_get(aActorID) {
return this._actors[aActorID];
},
has: function AP_has(aActorID) {
return aActorID in this._actors;
},
/**
* Returns true if the pool is empty.
*/
isEmpty: function AP_isEmpty() {
return Object.keys(this._actors).length == 0;
},
/**
* Remove an actor from the actor pool.
*/
removeActor: function AP_remove(aActor) {
delete this._actors[aActor.actorID];
delete this._cleanups[aActor.actorID];
},
/**
* Match the api expected by the protocol library.
*/
unmanage: function(aActor) {
return this.removeActor(aActor);
},
/**
* Run all actor cleanups.
*/
cleanup: function AP_cleanup() {
for each (let actor in this._cleanups) {
actor.disconnect();
}
this._cleanups = {};
}
}
/**
* Creates a DebuggerServerConnection.

View File

@ -8,8 +8,9 @@ const Cu = Components.utils;
const Cr = Components.results;
const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
const DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils.js");
const Services = devtools.require("Services");
const { ActorPool, createExtraActors, appendExtraActors } = devtools.require("devtools/server/actors/common");
const DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils.js");
const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
// Always log packets when running tests. runxpcshelltests.py will throw

View File

@ -107,8 +107,8 @@ TestTabActor.prototype = {
},
/* Support for DebuggerServer.addTabActor. */
_createExtraActors: CommonCreateExtraActors,
_appendExtraActors: CommonAppendExtraActors
_createExtraActors: createExtraActors,
_appendExtraActors: appendExtraActors
};
TestTabActor.prototype.requestTypes = {

View File

@ -4,10 +4,30 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
Components.utils.import("resource://gre/modules/NetUtil.jsm");
// TODO: Get rid of this code once the marionette server loads transport.js as
// an SDK module (see bug 1000814)
(function (factory) { // Module boilerplate
if (this.module && module.id.indexOf("transport") >= 0) { // require
factory(require, exports);
} else { // loadSubScript
if (this.require) {
factory(require, this);
} else {
const Cu = Components.utils;
const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
factory(devtools.require, this);
}
}
}).call(this, function (require, exports) {
let wantLogging = Services.prefs.getBoolPref("devtools.debugger.log");
"use strict";
const { Cc, Ci, Cr, Cu } = require("chrome");
const Services = require("Services");
const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
const { dumpn } = DevToolsUtils;
Cu.import("resource://gre/modules/NetUtil.jsm");
/**
* An adapter that handles data transfers between the debugger client and
@ -43,7 +63,7 @@ let wantLogging = Services.prefs.getBoolPref("devtools.debugger.log");
* ([length]:[packet]). The contents of the JSON packet are specified in
* the Remote Debugging Protocol specification.
*/
this.DebuggerTransport = function DebuggerTransport(aInput, aOutput)
function DebuggerTransport(aInput, aOutput)
{
this._input = aInput;
this._output = aOutput;
@ -98,7 +118,7 @@ DebuggerTransport.prototype = {
let written = 0;
try {
written = aStream.write(this._outgoing, this._outgoing.length);
} catch(e if e.result == Components.results.NS_BASE_STREAM_CLOSED) {
} catch(e if e.result == Cr.NS_BASE_STREAM_CLOSED) {
dumpn("Connection closed.");
this.close();
return;
@ -190,7 +210,7 @@ DebuggerTransport.prototype = {
return true;
}
if (wantLogging) {
if (dumpn.wantLogging) {
dumpn("Got: " + JSON.stringify(parsed, null, 2));
}
let self = this;
@ -206,6 +226,7 @@ DebuggerTransport.prototype = {
}
}
exports.DebuggerTransport = DebuggerTransport;
/**
* An adapter that handles data transfers between the debugger client and
@ -218,7 +239,7 @@ DebuggerTransport.prototype = {
*
* @see DebuggerTransport
*/
this.LocalDebuggerTransport = function LocalDebuggerTransport(aOther)
function LocalDebuggerTransport(aOther)
{
this.other = aOther;
this.hooks = null;
@ -238,7 +259,7 @@ LocalDebuggerTransport.prototype = {
*/
send: function LDT_send(aPacket) {
let serial = this._serial.count++;
if (wantLogging) {
if (dumpn.wantLogging) {
/* Check 'from' first, as 'echo' packets have both. */
if (aPacket.from) {
dumpn("Packet " + serial + " sent from " + uneval(aPacket.from));
@ -251,7 +272,7 @@ LocalDebuggerTransport.prototype = {
if (other) {
Services.tm.currentThread.dispatch(DevToolsUtils.makeInfallible(function() {
// Avoid the cost of JSON.stringify() when logging is disabled.
if (wantLogging) {
if (dumpn.wantLogging) {
dumpn("Received packet " + serial + ": " + JSON.stringify(aPacket, null, 2));
}
if (other.hooks) {
@ -276,7 +297,7 @@ LocalDebuggerTransport.prototype = {
try {
this.hooks.onClosed();
} catch(ex) {
Components.utils.reportError(ex);
Cu.reportError(ex);
}
this.hooks = null;
}
@ -305,6 +326,8 @@ LocalDebuggerTransport.prototype = {
}
};
exports.LocalDebuggerTransport = LocalDebuggerTransport;
/**
* A transport for the debugging protocol that uses nsIMessageSenders to
* exchange packets with servers running in child processes.
@ -320,7 +343,7 @@ LocalDebuggerTransport.prototype = {
* <prefix> is |aPrefix|, whose data is the protocol packet.
*/
function ChildDebuggerTransport(aSender, aPrefix) {
this._sender = aSender.QueryInterface(Components.interfaces.nsIMessageSender);
this._sender = aSender.QueryInterface(Ci.nsIMessageSender);
this._messageName = "debug:" + aPrefix + ":packet";
}
@ -351,3 +374,7 @@ ChildDebuggerTransport.prototype = {
this._sender.sendAsyncMessage(this._messageName, packet);
}
};
exports.ChildDebuggerTransport = ChildDebuggerTransport;
});

View File

@ -183,7 +183,7 @@ CommonNativeApp.prototype = {
// because the app isn't installed yet).
if (this.iconURI.scheme == "app") {
let zipUrl = OS.Path.toFileURI(OS.Path.join(aTmpDir,
"application.zip"));
this.zipFile));
let filePath = this.iconURI.QueryInterface(Ci.nsIURL).filePath;