dom/indexedDB/IDBDatabase.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=2 et sw=2 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "base/basictypes.h"
michael@0 8
michael@0 9 #include "IDBDatabase.h"
michael@0 10
michael@0 11 #include "mozilla/EventDispatcher.h"
michael@0 12 #include "mozilla/Mutex.h"
michael@0 13 #include "mozilla/storage.h"
michael@0 14 #include "mozilla/dom/ContentParent.h"
michael@0 15 #include "mozilla/dom/DOMStringList.h"
michael@0 16 #include "mozilla/dom/DOMStringListBinding.h"
michael@0 17 #include "mozilla/dom/quota/Client.h"
michael@0 18 #include "mozilla/dom/quota/QuotaManager.h"
michael@0 19 #include "nsJSUtils.h"
michael@0 20 #include "nsProxyRelease.h"
michael@0 21 #include "nsThreadUtils.h"
michael@0 22
michael@0 23 #include "AsyncConnectionHelper.h"
michael@0 24 #include "DatabaseInfo.h"
michael@0 25 #include "IDBEvents.h"
michael@0 26 #include "IDBFactory.h"
michael@0 27 #include "IDBFileHandle.h"
michael@0 28 #include "IDBIndex.h"
michael@0 29 #include "IDBObjectStore.h"
michael@0 30 #include "IDBTransaction.h"
michael@0 31 #include "IDBFactory.h"
michael@0 32 #include "ProfilerHelpers.h"
michael@0 33 #include "ReportInternalError.h"
michael@0 34 #include "TransactionThreadPool.h"
michael@0 35
michael@0 36 #include "ipc/IndexedDBChild.h"
michael@0 37 #include "ipc/IndexedDBParent.h"
michael@0 38
michael@0 39 #include "mozilla/dom/IDBDatabaseBinding.h"
michael@0 40
michael@0 41 USING_INDEXEDDB_NAMESPACE
michael@0 42 using mozilla::dom::ContentParent;
michael@0 43 using mozilla::dom::quota::AssertIsOnIOThread;
michael@0 44 using mozilla::dom::quota::Client;
michael@0 45 using mozilla::dom::quota::QuotaManager;
michael@0 46 using mozilla::ErrorResult;
michael@0 47 using namespace mozilla;
michael@0 48 using namespace mozilla::dom;
michael@0 49
michael@0 50 namespace {
michael@0 51
michael@0 52 class NoRequestDatabaseHelper : public AsyncConnectionHelper
michael@0 53 {
michael@0 54 public:
michael@0 55 NoRequestDatabaseHelper(IDBTransaction* aTransaction)
michael@0 56 : AsyncConnectionHelper(aTransaction, nullptr)
michael@0 57 {
michael@0 58 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
michael@0 59 NS_ASSERTION(aTransaction, "Null transaction!");
michael@0 60 }
michael@0 61
michael@0 62 virtual ChildProcessSendResult
michael@0 63 SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE;
michael@0 64
michael@0 65 virtual nsresult
michael@0 66 UnpackResponseFromParentProcess(const ResponseValue& aResponseValue)
michael@0 67 MOZ_OVERRIDE;
michael@0 68
michael@0 69 virtual nsresult OnSuccess() MOZ_OVERRIDE;
michael@0 70
michael@0 71 virtual void OnError() MOZ_OVERRIDE;
michael@0 72 };
michael@0 73
michael@0 74 class CreateObjectStoreHelper : public NoRequestDatabaseHelper
michael@0 75 {
michael@0 76 public:
michael@0 77 CreateObjectStoreHelper(IDBTransaction* aTransaction,
michael@0 78 IDBObjectStore* aObjectStore)
michael@0 79 : NoRequestDatabaseHelper(aTransaction), mObjectStore(aObjectStore)
michael@0 80 { }
michael@0 81
michael@0 82 virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection)
michael@0 83 MOZ_OVERRIDE;
michael@0 84
michael@0 85 virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE;
michael@0 86
michael@0 87 private:
michael@0 88 nsRefPtr<IDBObjectStore> mObjectStore;
michael@0 89 };
michael@0 90
michael@0 91 class DeleteObjectStoreHelper : public NoRequestDatabaseHelper
michael@0 92 {
michael@0 93 public:
michael@0 94 DeleteObjectStoreHelper(IDBTransaction* aTransaction,
michael@0 95 int64_t aObjectStoreId)
michael@0 96 : NoRequestDatabaseHelper(aTransaction), mObjectStoreId(aObjectStoreId)
michael@0 97 { }
michael@0 98
michael@0 99 virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection)
michael@0 100 MOZ_OVERRIDE;
michael@0 101
michael@0 102 private:
michael@0 103 // In-params.
michael@0 104 int64_t mObjectStoreId;
michael@0 105 };
michael@0 106
michael@0 107 class CreateFileHelper : public AsyncConnectionHelper
michael@0 108 {
michael@0 109 public:
michael@0 110 CreateFileHelper(IDBDatabase* aDatabase,
michael@0 111 IDBRequest* aRequest,
michael@0 112 const nsAString& aName,
michael@0 113 const nsAString& aType)
michael@0 114 : AsyncConnectionHelper(aDatabase, aRequest),
michael@0 115 mName(aName), mType(aType)
michael@0 116 { }
michael@0 117
michael@0 118 ~CreateFileHelper()
michael@0 119 { }
michael@0 120
michael@0 121 nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
michael@0 122 nsresult GetSuccessResult(JSContext* aCx,
michael@0 123 JS::MutableHandle<JS::Value> aVal);
michael@0 124 void ReleaseMainThreadObjects()
michael@0 125 {
michael@0 126 mFileInfo = nullptr;
michael@0 127 AsyncConnectionHelper::ReleaseMainThreadObjects();
michael@0 128 }
michael@0 129
michael@0 130 virtual ChildProcessSendResult SendResponseToChildProcess(
michael@0 131 nsresult aResultCode)
michael@0 132 MOZ_OVERRIDE
michael@0 133 {
michael@0 134 return Success_NotSent;
michael@0 135 }
michael@0 136
michael@0 137 virtual nsresult UnpackResponseFromParentProcess(
michael@0 138 const ResponseValue& aResponseValue)
michael@0 139 MOZ_OVERRIDE
michael@0 140 {
michael@0 141 MOZ_CRASH("Should never get here!");
michael@0 142 }
michael@0 143
michael@0 144 private:
michael@0 145 // In-params.
michael@0 146 nsString mName;
michael@0 147 nsString mType;
michael@0 148
michael@0 149 // Out-params.
michael@0 150 nsRefPtr<FileInfo> mFileInfo;
michael@0 151 };
michael@0 152
michael@0 153 class MOZ_STACK_CLASS AutoRemoveObjectStore
michael@0 154 {
michael@0 155 public:
michael@0 156 AutoRemoveObjectStore(DatabaseInfo* aInfo, const nsAString& aName)
michael@0 157 : mInfo(aInfo), mName(aName)
michael@0 158 { }
michael@0 159
michael@0 160 ~AutoRemoveObjectStore()
michael@0 161 {
michael@0 162 if (mInfo) {
michael@0 163 mInfo->RemoveObjectStore(mName);
michael@0 164 }
michael@0 165 }
michael@0 166
michael@0 167 void forget()
michael@0 168 {
michael@0 169 mInfo = nullptr;
michael@0 170 }
michael@0 171
michael@0 172 private:
michael@0 173 DatabaseInfo* mInfo;
michael@0 174 nsString mName;
michael@0 175 };
michael@0 176
michael@0 177 } // anonymous namespace
michael@0 178
michael@0 179 // static
michael@0 180 already_AddRefed<IDBDatabase>
michael@0 181 IDBDatabase::Create(IDBWrapperCache* aOwnerCache,
michael@0 182 IDBFactory* aFactory,
michael@0 183 already_AddRefed<DatabaseInfo> aDatabaseInfo,
michael@0 184 const nsACString& aASCIIOrigin,
michael@0 185 FileManager* aFileManager,
michael@0 186 mozilla::dom::ContentParent* aContentParent)
michael@0 187 {
michael@0 188 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 189 NS_ASSERTION(aFactory, "Null pointer!");
michael@0 190 NS_ASSERTION(!aASCIIOrigin.IsEmpty(), "Empty origin!");
michael@0 191
michael@0 192 nsRefPtr<DatabaseInfo> databaseInfo(aDatabaseInfo);
michael@0 193 NS_ASSERTION(databaseInfo, "Null pointer!");
michael@0 194
michael@0 195 nsRefPtr<IDBDatabase> db(new IDBDatabase(aOwnerCache));
michael@0 196
michael@0 197 db->SetScriptOwner(aOwnerCache->GetScriptOwner());
michael@0 198 db->mFactory = aFactory;
michael@0 199 db->mDatabaseId = databaseInfo->id;
michael@0 200 db->mName = databaseInfo->name;
michael@0 201 db->mFilePath = databaseInfo->filePath;
michael@0 202 db->mPersistenceType = databaseInfo->persistenceType;
michael@0 203 db->mGroup = databaseInfo->group;
michael@0 204 databaseInfo.swap(db->mDatabaseInfo);
michael@0 205 db->mASCIIOrigin = aASCIIOrigin;
michael@0 206 db->mFileManager = aFileManager;
michael@0 207 db->mContentParent = aContentParent;
michael@0 208
michael@0 209 QuotaManager* quotaManager = QuotaManager::Get();
michael@0 210 NS_ASSERTION(quotaManager, "This should never be null!");
michael@0 211
michael@0 212 db->mQuotaClient = quotaManager->GetClient(Client::IDB);
michael@0 213 NS_ASSERTION(db->mQuotaClient, "This shouldn't fail!");
michael@0 214
michael@0 215 if (!quotaManager->RegisterStorage(db)) {
michael@0 216 // Either out of memory or shutting down.
michael@0 217 return nullptr;
michael@0 218 }
michael@0 219
michael@0 220 db->mRegistered = true;
michael@0 221
michael@0 222 return db.forget();
michael@0 223 }
michael@0 224
michael@0 225 // static
michael@0 226 IDBDatabase*
michael@0 227 IDBDatabase::FromStorage(nsIOfflineStorage* aStorage)
michael@0 228 {
michael@0 229 return aStorage->GetClient()->GetType() == Client::IDB ?
michael@0 230 static_cast<IDBDatabase*>(aStorage) : nullptr;
michael@0 231 }
michael@0 232
michael@0 233 IDBDatabase::IDBDatabase(IDBWrapperCache* aOwnerCache)
michael@0 234 : IDBWrapperCache(aOwnerCache),
michael@0 235 mActorChild(nullptr),
michael@0 236 mActorParent(nullptr),
michael@0 237 mContentParent(nullptr),
michael@0 238 mInvalidated(false),
michael@0 239 mRegistered(false),
michael@0 240 mClosed(false),
michael@0 241 mRunningVersionChange(false)
michael@0 242 {
michael@0 243 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 244 }
michael@0 245
michael@0 246 IDBDatabase::~IDBDatabase()
michael@0 247 {
michael@0 248 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 249 }
michael@0 250
michael@0 251 void
michael@0 252 IDBDatabase::LastRelease()
michael@0 253 {
michael@0 254 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 255
michael@0 256 NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!");
michael@0 257 if (mActorChild) {
michael@0 258 NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
michael@0 259 mActorChild->Send__delete__(mActorChild);
michael@0 260 NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!");
michael@0 261 }
michael@0 262
michael@0 263 if (mRegistered) {
michael@0 264 CloseInternal(true);
michael@0 265
michael@0 266 QuotaManager* quotaManager = QuotaManager::Get();
michael@0 267 if (quotaManager) {
michael@0 268 quotaManager->UnregisterStorage(this);
michael@0 269 }
michael@0 270 mRegistered = false;
michael@0 271 }
michael@0 272 }
michael@0 273
michael@0 274 NS_IMETHODIMP_(void)
michael@0 275 IDBDatabase::Invalidate()
michael@0 276 {
michael@0 277 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 278
michael@0 279 InvalidateInternal(/* aIsDead */ false);
michael@0 280 }
michael@0 281
michael@0 282 void
michael@0 283 IDBDatabase::InvalidateInternal(bool aIsDead)
michael@0 284 {
michael@0 285 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 286
michael@0 287 if (IsInvalidated()) {
michael@0 288 return;
michael@0 289 }
michael@0 290
michael@0 291 mInvalidated = true;
michael@0 292
michael@0 293 // Make sure we're closed too.
michael@0 294 Close();
michael@0 295
michael@0 296 // When the IndexedDatabaseManager needs to invalidate databases, all it has
michael@0 297 // is an origin, so we call into the quota manager here to cancel any prompts
michael@0 298 // for our owner.
michael@0 299 nsPIDOMWindow* owner = GetOwner();
michael@0 300 if (owner) {
michael@0 301 QuotaManager::CancelPromptsForWindow(owner);
michael@0 302 }
michael@0 303
michael@0 304 // We want to forcefully remove in the child when the parent has invalidated
michael@0 305 // us in IPC mode because the database might no longer exist.
michael@0 306 // We don't want to forcefully remove in the parent when a child dies since
michael@0 307 // other child processes may be using the referenced DatabaseInfo.
michael@0 308 if (!aIsDead) {
michael@0 309 DatabaseInfo::Remove(mDatabaseId);
michael@0 310 }
michael@0 311
michael@0 312 // And let the child process know as well.
michael@0 313 if (mActorParent) {
michael@0 314 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
michael@0 315 mActorParent->Invalidate();
michael@0 316 }
michael@0 317 }
michael@0 318
michael@0 319 void
michael@0 320 IDBDatabase::DisconnectFromActorParent()
michael@0 321 {
michael@0 322 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
michael@0 323 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 324
michael@0 325 // Make sure we're closed too.
michael@0 326 Close();
michael@0 327
michael@0 328 // Kill any outstanding prompts.
michael@0 329 nsPIDOMWindow* owner = GetOwner();
michael@0 330 if (owner) {
michael@0 331 QuotaManager::CancelPromptsForWindow(owner);
michael@0 332 }
michael@0 333 }
michael@0 334
michael@0 335 void
michael@0 336 IDBDatabase::CloseInternal(bool aIsDead)
michael@0 337 {
michael@0 338 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 339
michael@0 340 if (!mClosed) {
michael@0 341 mClosed = true;
michael@0 342
michael@0 343 // If we're getting called from Unlink, avoid cloning the DatabaseInfo.
michael@0 344 {
michael@0 345 nsRefPtr<DatabaseInfo> previousInfo;
michael@0 346 mDatabaseInfo.swap(previousInfo);
michael@0 347
michael@0 348 if (!aIsDead) {
michael@0 349 mDatabaseInfo = previousInfo->Clone();
michael@0 350 }
michael@0 351 }
michael@0 352
michael@0 353 QuotaManager* quotaManager = QuotaManager::Get();
michael@0 354 if (quotaManager) {
michael@0 355 quotaManager->OnStorageClosed(this);
michael@0 356 }
michael@0 357
michael@0 358 // And let the parent process know as well.
michael@0 359 if (mActorChild && !IsInvalidated()) {
michael@0 360 NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
michael@0 361 mActorChild->SendClose(aIsDead);
michael@0 362 }
michael@0 363 }
michael@0 364 }
michael@0 365
michael@0 366 NS_IMETHODIMP_(bool)
michael@0 367 IDBDatabase::IsClosed()
michael@0 368 {
michael@0 369 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 370 return mClosed;
michael@0 371 }
michael@0 372
michael@0 373 void
michael@0 374 IDBDatabase::EnterSetVersionTransaction()
michael@0 375 {
michael@0 376 NS_ASSERTION(!mRunningVersionChange, "How did that happen?");
michael@0 377
michael@0 378 mPreviousDatabaseInfo = mDatabaseInfo->Clone();
michael@0 379
michael@0 380 mRunningVersionChange = true;
michael@0 381 }
michael@0 382
michael@0 383 void
michael@0 384 IDBDatabase::ExitSetVersionTransaction()
michael@0 385 {
michael@0 386 NS_ASSERTION(mRunningVersionChange, "How did that happen?");
michael@0 387
michael@0 388 mPreviousDatabaseInfo = nullptr;
michael@0 389
michael@0 390 mRunningVersionChange = false;
michael@0 391 }
michael@0 392
michael@0 393 void
michael@0 394 IDBDatabase::RevertToPreviousState()
michael@0 395 {
michael@0 396 mDatabaseInfo = mPreviousDatabaseInfo;
michael@0 397 mPreviousDatabaseInfo = nullptr;
michael@0 398 }
michael@0 399
michael@0 400 void
michael@0 401 IDBDatabase::OnUnlink()
michael@0 402 {
michael@0 403 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 404
michael@0 405 // We've been unlinked, at the very least we should be able to prevent further
michael@0 406 // transactions from starting and unblock any other SetVersion callers.
michael@0 407 CloseInternal(true);
michael@0 408
michael@0 409 // No reason for the QuotaManager to track us any longer.
michael@0 410 QuotaManager* quotaManager = QuotaManager::Get();
michael@0 411 if (mRegistered && quotaManager) {
michael@0 412 quotaManager->UnregisterStorage(this);
michael@0 413
michael@0 414 // Don't try to unregister again in the destructor.
michael@0 415 mRegistered = false;
michael@0 416 }
michael@0 417 }
michael@0 418
michael@0 419 already_AddRefed<IDBObjectStore>
michael@0 420 IDBDatabase::CreateObjectStoreInternal(IDBTransaction* aTransaction,
michael@0 421 const ObjectStoreInfoGuts& aInfo,
michael@0 422 ErrorResult& aRv)
michael@0 423 {
michael@0 424 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 425 NS_ASSERTION(aTransaction, "Null transaction!");
michael@0 426
michael@0 427 DatabaseInfo* databaseInfo = aTransaction->DBInfo();
michael@0 428
michael@0 429 nsRefPtr<ObjectStoreInfo> newInfo = new ObjectStoreInfo();
michael@0 430 *static_cast<ObjectStoreInfoGuts*>(newInfo.get()) = aInfo;
michael@0 431
michael@0 432 newInfo->nextAutoIncrementId = aInfo.autoIncrement ? 1 : 0;
michael@0 433 newInfo->comittedAutoIncrementId = newInfo->nextAutoIncrementId;
michael@0 434
michael@0 435 if (!databaseInfo->PutObjectStore(newInfo)) {
michael@0 436 IDB_WARNING("Put failed!");
michael@0 437 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 438 return nullptr;
michael@0 439 }
michael@0 440
michael@0 441 // Don't leave this in the hash if we fail below!
michael@0 442 AutoRemoveObjectStore autoRemove(databaseInfo, newInfo->name);
michael@0 443
michael@0 444 nsRefPtr<IDBObjectStore> objectStore =
michael@0 445 aTransaction->GetOrCreateObjectStore(newInfo->name, newInfo, true);
michael@0 446 if (!objectStore) {
michael@0 447 IDB_WARNING("Failed to get objectStore!");
michael@0 448 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 449 return nullptr;
michael@0 450 }
michael@0 451
michael@0 452 if (IndexedDatabaseManager::IsMainProcess()) {
michael@0 453 nsRefPtr<CreateObjectStoreHelper> helper =
michael@0 454 new CreateObjectStoreHelper(aTransaction, objectStore);
michael@0 455
michael@0 456 nsresult rv = helper->DispatchToTransactionPool();
michael@0 457 if (NS_FAILED(rv)) {
michael@0 458 IDB_WARNING("Failed to dispatch!");
michael@0 459 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 460 return nullptr;
michael@0 461 }
michael@0 462 }
michael@0 463
michael@0 464 autoRemove.forget();
michael@0 465
michael@0 466 IDB_PROFILER_MARK("IndexedDB Pseudo-request: "
michael@0 467 "database(%s).transaction(%s).createObjectStore(%s)",
michael@0 468 "MT IDBDatabase.createObjectStore()",
michael@0 469 IDB_PROFILER_STRING(this),
michael@0 470 IDB_PROFILER_STRING(aTransaction),
michael@0 471 IDB_PROFILER_STRING(objectStore));
michael@0 472
michael@0 473 return objectStore.forget();
michael@0 474 }
michael@0 475
michael@0 476 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBDatabase)
michael@0 477
michael@0 478 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBDatabase, IDBWrapperCache)
michael@0 479 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFactory)
michael@0 480 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
michael@0 481
michael@0 482 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBDatabase, IDBWrapperCache)
michael@0 483 // Don't unlink mFactory!
michael@0 484
michael@0 485 // Do some cleanup.
michael@0 486 tmp->OnUnlink();
michael@0 487 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
michael@0 488
michael@0 489 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBDatabase)
michael@0 490 NS_INTERFACE_MAP_ENTRY(nsIFileStorage)
michael@0 491 NS_INTERFACE_MAP_ENTRY(nsIOfflineStorage)
michael@0 492 NS_INTERFACE_MAP_END_INHERITING(IDBWrapperCache)
michael@0 493
michael@0 494 NS_IMPL_ADDREF_INHERITED(IDBDatabase, IDBWrapperCache)
michael@0 495 NS_IMPL_RELEASE_INHERITED(IDBDatabase, IDBWrapperCache)
michael@0 496
michael@0 497 JSObject*
michael@0 498 IDBDatabase::WrapObject(JSContext* aCx)
michael@0 499 {
michael@0 500 return IDBDatabaseBinding::Wrap(aCx, this);
michael@0 501 }
michael@0 502
michael@0 503 uint64_t
michael@0 504 IDBDatabase::Version() const
michael@0 505 {
michael@0 506 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 507 DatabaseInfo* info = Info();
michael@0 508 return info->version;
michael@0 509 }
michael@0 510
michael@0 511 already_AddRefed<DOMStringList>
michael@0 512 IDBDatabase::GetObjectStoreNames(ErrorResult& aRv) const
michael@0 513 {
michael@0 514 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 515
michael@0 516 DatabaseInfo* info = Info();
michael@0 517
michael@0 518 nsRefPtr<DOMStringList> list(new DOMStringList());
michael@0 519 if (!info->GetObjectStoreNames(list->StringArray())) {
michael@0 520 IDB_WARNING("Couldn't get names!");
michael@0 521 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 522 return nullptr;
michael@0 523 }
michael@0 524
michael@0 525 return list.forget();
michael@0 526 }
michael@0 527
michael@0 528 already_AddRefed<IDBObjectStore>
michael@0 529 IDBDatabase::CreateObjectStore(
michael@0 530 JSContext* aCx, const nsAString& aName,
michael@0 531 const IDBObjectStoreParameters& aOptionalParameters,
michael@0 532 ErrorResult& aRv)
michael@0 533 {
michael@0 534 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 535
michael@0 536 IDBTransaction* transaction = AsyncConnectionHelper::GetCurrentTransaction();
michael@0 537
michael@0 538 if (!transaction ||
michael@0 539 transaction->GetMode() != IDBTransaction::VERSION_CHANGE) {
michael@0 540 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
michael@0 541 return nullptr;
michael@0 542 }
michael@0 543
michael@0 544 DatabaseInfo* databaseInfo = transaction->DBInfo();
michael@0 545
michael@0 546 KeyPath keyPath(0);
michael@0 547 if (NS_FAILED(KeyPath::Parse(aCx, aOptionalParameters.mKeyPath, &keyPath))) {
michael@0 548 aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
michael@0 549 return nullptr;
michael@0 550 }
michael@0 551
michael@0 552 if (databaseInfo->ContainsStoreName(aName)) {
michael@0 553 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR);
michael@0 554 return nullptr;
michael@0 555 }
michael@0 556
michael@0 557 if (!keyPath.IsAllowedForObjectStore(aOptionalParameters.mAutoIncrement)) {
michael@0 558 aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
michael@0 559 return nullptr;
michael@0 560 }
michael@0 561
michael@0 562 ObjectStoreInfoGuts guts;
michael@0 563
michael@0 564 guts.name = aName;
michael@0 565 guts.id = databaseInfo->nextObjectStoreId++;
michael@0 566 guts.keyPath = keyPath;
michael@0 567 guts.autoIncrement = aOptionalParameters.mAutoIncrement;
michael@0 568
michael@0 569 return CreateObjectStoreInternal(transaction, guts, aRv);
michael@0 570 }
michael@0 571
michael@0 572 void
michael@0 573 IDBDatabase::DeleteObjectStore(const nsAString& aName, ErrorResult& aRv)
michael@0 574 {
michael@0 575 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 576
michael@0 577 IDBTransaction* transaction = AsyncConnectionHelper::GetCurrentTransaction();
michael@0 578
michael@0 579 if (!transaction ||
michael@0 580 transaction->GetMode() != IDBTransaction::VERSION_CHANGE) {
michael@0 581 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
michael@0 582 return;
michael@0 583 }
michael@0 584
michael@0 585 DatabaseInfo* info = transaction->DBInfo();
michael@0 586 ObjectStoreInfo* objectStoreInfo = info->GetObjectStore(aName);
michael@0 587 if (!objectStoreInfo) {
michael@0 588 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR);
michael@0 589 return;
michael@0 590 }
michael@0 591
michael@0 592 if (IndexedDatabaseManager::IsMainProcess()) {
michael@0 593 nsRefPtr<DeleteObjectStoreHelper> helper =
michael@0 594 new DeleteObjectStoreHelper(transaction, objectStoreInfo->id);
michael@0 595
michael@0 596 nsresult rv = helper->DispatchToTransactionPool();
michael@0 597 if (NS_FAILED(rv)) {
michael@0 598 IDB_WARNING("Failed to dispatch!");
michael@0 599 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 600 return;
michael@0 601 }
michael@0 602 }
michael@0 603 else {
michael@0 604 IndexedDBTransactionChild* actor = transaction->GetActorChild();
michael@0 605 NS_ASSERTION(actor, "Must have an actor here!");
michael@0 606
michael@0 607 actor->SendDeleteObjectStore(nsString(aName));
michael@0 608 }
michael@0 609
michael@0 610 transaction->RemoveObjectStore(aName);
michael@0 611
michael@0 612 IDB_PROFILER_MARK("IndexedDB Pseudo-request: "
michael@0 613 "database(%s).transaction(%s).deleteObjectStore(\"%s\")",
michael@0 614 "MT IDBDatabase.deleteObjectStore()",
michael@0 615 IDB_PROFILER_STRING(this),
michael@0 616 IDB_PROFILER_STRING(transaction),
michael@0 617 NS_ConvertUTF16toUTF8(aName).get());
michael@0 618 }
michael@0 619
michael@0 620 already_AddRefed<indexedDB::IDBTransaction>
michael@0 621 IDBDatabase::Transaction(const Sequence<nsString>& aStoreNames,
michael@0 622 IDBTransactionMode aMode, ErrorResult& aRv)
michael@0 623 {
michael@0 624 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 625
michael@0 626 if (QuotaManager::IsShuttingDown()) {
michael@0 627 IDB_REPORT_INTERNAL_ERR();
michael@0 628 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 629 return nullptr;
michael@0 630 }
michael@0 631
michael@0 632 if (mClosed) {
michael@0 633 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
michael@0 634 return nullptr;
michael@0 635 }
michael@0 636
michael@0 637 if (mRunningVersionChange) {
michael@0 638 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
michael@0 639 return nullptr;
michael@0 640 }
michael@0 641
michael@0 642 if (aStoreNames.IsEmpty()) {
michael@0 643 aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
michael@0 644 return nullptr;
michael@0 645 }
michael@0 646
michael@0 647 IDBTransaction::Mode transactionMode = IDBTransaction::READ_ONLY;
michael@0 648 switch (aMode) {
michael@0 649 case IDBTransactionMode::Readonly:
michael@0 650 transactionMode = IDBTransaction::READ_ONLY;
michael@0 651 break;
michael@0 652 case IDBTransactionMode::Readwrite:
michael@0 653 transactionMode = IDBTransaction::READ_WRITE;
michael@0 654 break;
michael@0 655 case IDBTransactionMode::Versionchange:
michael@0 656 transactionMode = IDBTransaction::VERSION_CHANGE;
michael@0 657 break;
michael@0 658 default:
michael@0 659 MOZ_CRASH("Unknown mode!");
michael@0 660 }
michael@0 661
michael@0 662 // Now check to make sure the object store names we collected actually exist.
michael@0 663 DatabaseInfo* info = Info();
michael@0 664 for (uint32_t index = 0; index < aStoreNames.Length(); index++) {
michael@0 665 if (!info->ContainsStoreName(aStoreNames[index])) {
michael@0 666 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR);
michael@0 667 return nullptr;
michael@0 668 }
michael@0 669 }
michael@0 670
michael@0 671 nsRefPtr<IDBTransaction> transaction =
michael@0 672 IDBTransaction::Create(this, aStoreNames, transactionMode, false);
michael@0 673 if (!transaction) {
michael@0 674 IDB_WARNING("Failed to create the transaction!");
michael@0 675 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 676 return nullptr;
michael@0 677 }
michael@0 678
michael@0 679 IDB_PROFILER_MARK("IndexedDB Transaction %llu: database(%s).transaction(%s)",
michael@0 680 "IDBTransaction[%llu] MT Started",
michael@0 681 transaction->GetSerialNumber(), IDB_PROFILER_STRING(this),
michael@0 682 IDB_PROFILER_STRING(transaction));
michael@0 683
michael@0 684 return transaction.forget();
michael@0 685 }
michael@0 686
michael@0 687 already_AddRefed<IDBRequest>
michael@0 688 IDBDatabase::MozCreateFileHandle(const nsAString& aName,
michael@0 689 const Optional<nsAString>& aType,
michael@0 690 ErrorResult& aRv)
michael@0 691 {
michael@0 692 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 693
michael@0 694 if (!IndexedDatabaseManager::IsMainProcess()) {
michael@0 695 IDB_WARNING("Not supported yet!");
michael@0 696 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 697 return nullptr;
michael@0 698 }
michael@0 699
michael@0 700 if (QuotaManager::IsShuttingDown()) {
michael@0 701 IDB_REPORT_INTERNAL_ERR();
michael@0 702 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 703 return nullptr;
michael@0 704 }
michael@0 705
michael@0 706 if (mClosed) {
michael@0 707 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
michael@0 708 return nullptr;
michael@0 709 }
michael@0 710
michael@0 711 nsRefPtr<IDBRequest> request = IDBRequest::Create(this, nullptr);
michael@0 712
michael@0 713 nsRefPtr<CreateFileHelper> helper =
michael@0 714 new CreateFileHelper(this, request, aName,
michael@0 715 aType.WasPassed() ? aType.Value() : EmptyString());
michael@0 716
michael@0 717 QuotaManager* quotaManager = QuotaManager::Get();
michael@0 718 NS_ASSERTION(quotaManager, "We should definitely have a manager here");
michael@0 719
michael@0 720 nsresult rv = helper->Dispatch(quotaManager->IOThread());
michael@0 721 if (NS_FAILED(rv)) {
michael@0 722 IDB_WARNING("Failed to dispatch!");
michael@0 723 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 724 return nullptr;
michael@0 725 }
michael@0 726
michael@0 727 return request.forget();
michael@0 728 }
michael@0 729
michael@0 730 NS_IMETHODIMP
michael@0 731 IDBDatabase::Close()
michael@0 732 {
michael@0 733 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 734
michael@0 735 CloseInternal(false);
michael@0 736
michael@0 737 NS_ASSERTION(mClosed, "Should have set the closed flag!");
michael@0 738
michael@0 739 return NS_OK;
michael@0 740 }
michael@0 741
michael@0 742 NS_IMETHODIMP_(const nsACString&)
michael@0 743 IDBDatabase::Id()
michael@0 744 {
michael@0 745 return mDatabaseId;
michael@0 746 }
michael@0 747
michael@0 748 NS_IMETHODIMP_(bool)
michael@0 749 IDBDatabase::IsInvalidated()
michael@0 750 {
michael@0 751 return mInvalidated;
michael@0 752 }
michael@0 753
michael@0 754 NS_IMETHODIMP_(bool)
michael@0 755 IDBDatabase::IsShuttingDown()
michael@0 756 {
michael@0 757 return QuotaManager::IsShuttingDown();
michael@0 758 }
michael@0 759
michael@0 760 NS_IMETHODIMP_(void)
michael@0 761 IDBDatabase::SetThreadLocals()
michael@0 762 {
michael@0 763 NS_ASSERTION(GetOwner(), "Should have owner!");
michael@0 764 QuotaManager::SetCurrentWindow(GetOwner());
michael@0 765 }
michael@0 766
michael@0 767 NS_IMETHODIMP_(void)
michael@0 768 IDBDatabase::UnsetThreadLocals()
michael@0 769 {
michael@0 770 QuotaManager::SetCurrentWindow(nullptr);
michael@0 771 }
michael@0 772
michael@0 773 NS_IMETHODIMP_(mozilla::dom::quota::Client*)
michael@0 774 IDBDatabase::GetClient()
michael@0 775 {
michael@0 776 return mQuotaClient;
michael@0 777 }
michael@0 778
michael@0 779 NS_IMETHODIMP_(bool)
michael@0 780 IDBDatabase::IsOwned(nsPIDOMWindow* aOwner)
michael@0 781 {
michael@0 782 return GetOwner() == aOwner;
michael@0 783 }
michael@0 784
michael@0 785 NS_IMETHODIMP_(const nsACString&)
michael@0 786 IDBDatabase::Origin()
michael@0 787 {
michael@0 788 return mASCIIOrigin;
michael@0 789 }
michael@0 790
michael@0 791 nsresult
michael@0 792 IDBDatabase::PostHandleEvent(EventChainPostVisitor& aVisitor)
michael@0 793 {
michael@0 794 return IndexedDatabaseManager::FireWindowOnError(GetOwner(), aVisitor);
michael@0 795 }
michael@0 796
michael@0 797 AsyncConnectionHelper::ChildProcessSendResult
michael@0 798 NoRequestDatabaseHelper::SendResponseToChildProcess(nsresult aResultCode)
michael@0 799 {
michael@0 800 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
michael@0 801 return Success_NotSent;
michael@0 802 }
michael@0 803
michael@0 804 nsresult
michael@0 805 NoRequestDatabaseHelper::UnpackResponseFromParentProcess(
michael@0 806 const ResponseValue& aResponseValue)
michael@0 807 {
michael@0 808 MOZ_CRASH("Should never get here!");
michael@0 809 }
michael@0 810
michael@0 811 nsresult
michael@0 812 NoRequestDatabaseHelper::OnSuccess()
michael@0 813 {
michael@0 814 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
michael@0 815 return NS_OK;
michael@0 816 }
michael@0 817
michael@0 818 void
michael@0 819 NoRequestDatabaseHelper::OnError()
michael@0 820 {
michael@0 821 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
michael@0 822 mTransaction->Abort(GetResultCode());
michael@0 823 }
michael@0 824
michael@0 825 nsresult
michael@0 826 CreateObjectStoreHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
michael@0 827 {
michael@0 828 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
michael@0 829 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
michael@0 830
michael@0 831 PROFILER_LABEL("IndexedDB", "CreateObjectStoreHelper::DoDatabaseWork");
michael@0 832
michael@0 833 if (IndexedDatabaseManager::InLowDiskSpaceMode()) {
michael@0 834 NS_WARNING("Refusing to create additional objectStore because disk space "
michael@0 835 "is low!");
michael@0 836 return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
michael@0 837 }
michael@0 838
michael@0 839 nsCOMPtr<mozIStorageStatement> stmt =
michael@0 840 mTransaction->GetCachedStatement(NS_LITERAL_CSTRING(
michael@0 841 "INSERT INTO object_store (id, auto_increment, name, key_path) "
michael@0 842 "VALUES (:id, :auto_increment, :name, :key_path)"
michael@0 843 ));
michael@0 844 IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 845
michael@0 846 mozStorageStatementScoper scoper(stmt);
michael@0 847
michael@0 848 nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"),
michael@0 849 mObjectStore->Id());
michael@0 850 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 851
michael@0 852 rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("auto_increment"),
michael@0 853 mObjectStore->IsAutoIncrement() ? 1 : 0);
michael@0 854 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 855
michael@0 856 rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mObjectStore->Name());
michael@0 857 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 858
michael@0 859 const KeyPath& keyPath = mObjectStore->GetKeyPath();
michael@0 860 if (keyPath.IsValid()) {
michael@0 861 nsAutoString keyPathSerialization;
michael@0 862 keyPath.SerializeToString(keyPathSerialization);
michael@0 863 rv = stmt->BindStringByName(NS_LITERAL_CSTRING("key_path"),
michael@0 864 keyPathSerialization);
michael@0 865 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 866 }
michael@0 867 else {
michael@0 868 rv = stmt->BindNullByName(NS_LITERAL_CSTRING("key_path"));
michael@0 869 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 870 }
michael@0 871
michael@0 872 rv = stmt->Execute();
michael@0 873 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 874
michael@0 875 return NS_OK;
michael@0 876 }
michael@0 877
michael@0 878 void
michael@0 879 CreateObjectStoreHelper::ReleaseMainThreadObjects()
michael@0 880 {
michael@0 881 mObjectStore = nullptr;
michael@0 882 NoRequestDatabaseHelper::ReleaseMainThreadObjects();
michael@0 883 }
michael@0 884
michael@0 885 nsresult
michael@0 886 DeleteObjectStoreHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
michael@0 887 {
michael@0 888 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
michael@0 889 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
michael@0 890
michael@0 891 PROFILER_LABEL("IndexedDB", "DeleteObjectStoreHelper::DoDatabaseWork");
michael@0 892
michael@0 893 nsCOMPtr<mozIStorageStatement> stmt =
michael@0 894 mTransaction->GetCachedStatement(NS_LITERAL_CSTRING(
michael@0 895 "DELETE FROM object_store "
michael@0 896 "WHERE id = :id "
michael@0 897 ));
michael@0 898 IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 899
michael@0 900 mozStorageStatementScoper scoper(stmt);
michael@0 901
michael@0 902 nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mObjectStoreId);
michael@0 903 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 904
michael@0 905 rv = stmt->Execute();
michael@0 906 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 907
michael@0 908 return NS_OK;
michael@0 909 }
michael@0 910
michael@0 911 nsresult
michael@0 912 CreateFileHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
michael@0 913 {
michael@0 914 AssertIsOnIOThread();
michael@0 915 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
michael@0 916
michael@0 917 PROFILER_LABEL("IndexedDB", "CreateFileHelper::DoDatabaseWork");
michael@0 918
michael@0 919 if (IndexedDatabaseManager::InLowDiskSpaceMode()) {
michael@0 920 NS_WARNING("Refusing to create file because disk space is low!");
michael@0 921 return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
michael@0 922 }
michael@0 923
michael@0 924 FileManager* fileManager = mDatabase->Manager();
michael@0 925
michael@0 926 mFileInfo = fileManager->GetNewFileInfo();
michael@0 927 IDB_ENSURE_TRUE(mFileInfo, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 928
michael@0 929 const int64_t& fileId = mFileInfo->Id();
michael@0 930
michael@0 931 nsCOMPtr<nsIFile> directory = fileManager->EnsureJournalDirectory();
michael@0 932 NS_ENSURE_TRUE(directory, NS_ERROR_FAILURE);
michael@0 933
michael@0 934 nsCOMPtr<nsIFile> file = fileManager->GetFileForId(directory, fileId);
michael@0 935 NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
michael@0 936
michael@0 937 nsresult rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
michael@0 938 NS_ENSURE_SUCCESS(rv, rv);
michael@0 939
michael@0 940 directory = fileManager->GetDirectory();
michael@0 941 IDB_ENSURE_TRUE(directory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 942
michael@0 943 file = fileManager->GetFileForId(directory, fileId);
michael@0 944 IDB_ENSURE_TRUE(file, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 945
michael@0 946 rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
michael@0 947 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 948
michael@0 949 return NS_OK;
michael@0 950 }
michael@0 951
michael@0 952 nsresult
michael@0 953 CreateFileHelper::GetSuccessResult(JSContext* aCx,
michael@0 954 JS::MutableHandle<JS::Value> aVal)
michael@0 955 {
michael@0 956 nsRefPtr<IDBFileHandle> fileHandle =
michael@0 957 IDBFileHandle::Create(mDatabase, mName, mType, mFileInfo.forget());
michael@0 958 IDB_ENSURE_TRUE(fileHandle, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 959
michael@0 960 return WrapNative(aCx, NS_ISUPPORTS_CAST(EventTarget*, fileHandle), aVal);
michael@0 961 }

mercurial