already_AddRefed<Promise> WorkerDataStore::GetLength(JSContext* aCx, ErrorResult& aRv) { WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx); MOZ_ASSERT(workerPrivate); workerPrivate->AssertIsOnWorkerThread(); RefPtr<Promise> promise = Promise::Create(workerPrivate->GlobalScope(), aRv); if (aRv.Failed()) { return nullptr; } RefPtr<DataStoreGetLengthRunnable> runnable = new DataStoreGetLengthRunnable(workerPrivate, mBackingStore, promise); runnable->Dispatch(aRv); if (aRv.Failed()) { return nullptr; } if (runnable->Failed()) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; } return promise.forget(); }
already_AddRefed<Promise> WorkerDataStore::Remove(JSContext* aCx, const StringOrUnsignedLong& aId, const nsAString& aRevisionId, ErrorResult& aRv) { WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx); MOZ_ASSERT(workerPrivate); workerPrivate->AssertIsOnWorkerThread(); RefPtr<Promise> promise = Promise::Create(workerPrivate->GlobalScope(), aRv); if (aRv.Failed()) { return nullptr; } RefPtr<DataStoreRemoveRunnable> runnable = new DataStoreRemoveRunnable(workerPrivate, mBackingStore, promise, aId, aRevisionId); runnable->Dispatch(aRv); if (aRv.Failed()) { return nullptr; } if (runnable->Failed()) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; } return promise.forget(); }
already_AddRefed<WorkerDataStoreCursor> WorkerDataStore::Sync(JSContext* aCx, const nsAString& aRevisionId, ErrorResult& aRv) { WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx); MOZ_ASSERT(workerPrivate); workerPrivate->AssertIsOnWorkerThread(); // Create a WorkerDataStoreCursor on the worker. Note that we need to pass // this WorkerDataStore into the WorkerDataStoreCursor, so that it can keep // track of which WorkerDataStore owns the WorkerDataStoreCursor. RefPtr<WorkerDataStoreCursor> workerCursor = new WorkerDataStoreCursor(this); // DataStoreSyncStoreRunnable will point the WorkerDataStoreCursor to the // DataStoreCursor created on the main thread. RefPtr<DataStoreSyncStoreRunnable> runnable = new DataStoreSyncStoreRunnable(workerPrivate, mBackingStore, workerCursor, aRevisionId); runnable->Dispatch(aRv); if (aRv.Failed()) { return nullptr; } if (runnable->Failed()) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; } return workerCursor.forget(); }
static JSObject* GetDataStoresStructuredCloneCallbacksRead(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag, uint32_t aData, void* aClosure) { WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx); MOZ_ASSERT(workerPrivate); workerPrivate->AssertIsOnWorkerThread(); if (aTag != WORKER_DATA_STORES_TAG) { MOZ_ASSERT(false, "aTag must be WORKER_DATA_STORES_TAG!"); return nullptr; } NS_ASSERTION(!aData, "aData should be empty"); // Read the holder from the buffer, which points to the data store. nsMainThreadPtrHolder<DataStore>* dataStoreholder; if (!JS_ReadBytes(aReader, &dataStoreholder, sizeof(dataStoreholder))) { MOZ_ASSERT(false, "cannot read bytes for dataStoreholder!"); return nullptr; } // Protect workerStoreObj from moving GC during ~nsRefPtr. JS::Rooted<JSObject*> workerStoreObj(aCx, nullptr); { nsRefPtr<WorkerDataStore> workerStore = new WorkerDataStore(workerPrivate->GlobalScope()); nsMainThreadPtrHandle<DataStore> backingStore(dataStoreholder); // When we're on the worker thread, prepare a DataStoreChangeEventProxy. nsRefPtr<DataStoreChangeEventProxy> eventProxy = new DataStoreChangeEventProxy(workerPrivate, workerStore); // Add the DataStoreChangeEventProxy as an event listener on the main thread. nsRefPtr<DataStoreAddEventListenerRunnable> runnable = new DataStoreAddEventListenerRunnable(workerPrivate, backingStore, eventProxy); runnable->Dispatch(aCx); // Point WorkerDataStore to DataStore. workerStore->SetBackingDataStore(backingStore); JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx)); if (!global) { MOZ_ASSERT(false, "cannot get global!"); } else { workerStoreObj = workerStore->WrapObject(aCx); if (!JS_WrapObject(aCx, &workerStoreObj)) { MOZ_ASSERT(false, "cannot wrap object for workerStoreObj!"); workerStoreObj = nullptr; } } } return workerStoreObj; }
already_AddRefed<Promise> WorkerDataStore::Add(JSContext* aCx, JS::Handle<JS::Value> aObj, const Optional<StringOrUnsignedLong>& aId, const nsAString& aRevisionId, ErrorResult& aRv) { WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx); MOZ_ASSERT(workerPrivate); workerPrivate->AssertIsOnWorkerThread(); nsRefPtr<Promise> promise = Promise::Create(workerPrivate->GlobalScope(), aRv); if (aRv.Failed()) { return nullptr; } nsRefPtr<DataStoreAddRunnable> runnable = new DataStoreAddRunnable(workerPrivate, mBackingStore, promise, aCx, aObj, aId, aRevisionId, aRv); runnable->Dispatch(aCx); return promise.forget(); }
/* static */ bool OffscreenCanvas::PrefEnabled(JSContext* aCx, JSObject* aObj) { if (NS_IsMainThread()) { return Preferences::GetBool("gfx.offscreencanvas.enabled"); } else { WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx); MOZ_ASSERT(workerPrivate); return workerPrivate->OffscreenCanvasEnabled(); } }
already_AddRefed<WorkerDataStore> WorkerDataStore::Constructor(GlobalObject& aGlobal, ErrorResult& aRv) { JSContext* cx = aGlobal.GetContext(); WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx); MOZ_ASSERT(workerPrivate); workerPrivate->AssertIsOnWorkerThread(); nsRefPtr<WorkerDataStore> store = new WorkerDataStore(workerPrivate->GlobalScope()); return store.forget(); }
/* static */ bool ChromeWorker::WorkerAvailable(JSContext* aCx, JSObject* /* unused */) { // Chrome is always allowed to use workers, and content is never // allowed to use ChromeWorker, so all we have to check is the // caller. However, chrome workers apparently might not have a // system principal, so we have to check for them manually. if (NS_IsMainThread()) { return nsContentUtils::IsSystemCaller(aCx); } return GetWorkerPrivateFromContext(aCx)->IsChromeWorker(); }
bool LoadWorkerScript(JSContext* aCx) { WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx); NS_ASSERTION(worker, "This should never be null!"); nsTArray<ScriptLoadInfo> loadInfos; ScriptLoadInfo* info = loadInfos.AppendElement(); info->mURL = worker->ScriptURL(); return LoadAllScripts(aCx, worker, loadInfos, true); }
bool WorkerDataStore::GetReadOnly(JSContext* aCx, ErrorResult& aRv) { WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx); MOZ_ASSERT(workerPrivate); workerPrivate->AssertIsOnWorkerThread(); nsRefPtr<DataStoreGetReadOnlyRunnable> runnable = new DataStoreGetReadOnlyRunnable(workerPrivate, mBackingStore, aRv); runnable->Dispatch(aCx); return runnable->mReadOnly; }
void WorkerDataStore::GetOwner(JSContext* aCx, nsAString& aOwner, ErrorResult& aRv) { WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx); MOZ_ASSERT(workerPrivate); workerPrivate->AssertIsOnWorkerThread(); RefPtr<DataStoreGetStringRunnable> runnable = new DataStoreGetStringRunnable(workerPrivate, mBackingStore, &DataStore::GetOwner, aOwner); runnable->Dispatch(aRv); }
/* static */ already_AddRefed<FileReader> FileReader::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv) { nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); WorkerPrivate* workerPrivate = nullptr; if (!NS_IsMainThread()) { JSContext* cx = aGlobal.Context(); workerPrivate = GetWorkerPrivateFromContext(cx); MOZ_ASSERT(workerPrivate); } RefPtr<FileReader> fileReader = new FileReader(global, workerPrivate); return fileReader.forget(); }
bool WorkerDataStore::GetReadOnly(JSContext* aCx, ErrorResult& aRv) { WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx); MOZ_ASSERT(workerPrivate); workerPrivate->AssertIsOnWorkerThread(); RefPtr<DataStoreGetReadOnlyRunnable> runnable = new DataStoreGetReadOnlyRunnable(workerPrivate, mBackingStore); runnable->Dispatch(aRv); if (aRv.Failed()) { return true; // To be safe, I guess. } return runnable->mReadOnly; }
void FetchStreamReader::ReportErrorToConsole(JSContext* aCx, JS::Handle<JS::Value> aValue) { nsCString sourceSpec; uint32_t line = 0; uint32_t column = 0; nsString valueString; nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column, valueString); nsTArray<nsString> params; params.AppendElement(valueString); RefPtr<ConsoleReportCollector> reporter = new ConsoleReportCollector(); reporter->AddConsoleReport(nsIScriptError::errorFlag, NS_LITERAL_CSTRING("ReadableStreamReader.read"), nsContentUtils::eDOM_PROPERTIES, sourceSpec, line, column, NS_LITERAL_CSTRING("ReadableStreamReadingFailed"), params); uint64_t innerWindowId = 0; if (NS_IsMainThread()) { nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mGlobal); if (window) { innerWindowId = window->WindowID(); } reporter->FlushReportsToConsole(innerWindowId); return; } WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx); if (workerPrivate) { innerWindowId = workerPrivate->WindowID(); } RefPtr<Runnable> r = NS_NewRunnableFunction( "FetchStreamReader::ReportErrorToConsole", [reporter, innerWindowId] () { reporter->FlushReportsToConsole(innerWindowId); }); workerPrivate->DispatchToMainThread(r.forget()); }
already_AddRefed<Promise> WorkerNavigator::GetDataStores(JSContext* aCx, const nsAString& aName, const nsAString& aOwner, ErrorResult& aRv) { WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx); MOZ_ASSERT(workerPrivate); workerPrivate->AssertIsOnWorkerThread(); nsRefPtr<Promise> promise = Promise::Create(workerPrivate->GlobalScope(), aRv); if (aRv.Failed()) { return nullptr; } nsRefPtr<NavigatorGetDataStoresRunnable> runnable = new NavigatorGetDataStoresRunnable(workerPrivate, promise, aName, aOwner, aRv); runnable->Dispatch(aCx); return promise.forget(); }
already_AddRefed<Promise> WorkerDataStore::Put(JSContext* aCx, JS::Handle<JS::Value> aObj, const StringOrUnsignedLong& aId, const nsAString& aRevisionId, ErrorResult& aRv) { WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx); MOZ_ASSERT(workerPrivate); workerPrivate->AssertIsOnWorkerThread(); RefPtr<Promise> promise = Promise::Create(workerPrivate->GlobalScope(), aRv); if (aRv.Failed()) { return nullptr; } RefPtr<DataStorePutRunnable> runnable = new DataStorePutRunnable(workerPrivate, mBackingStore, promise, aId, aRevisionId); runnable->Write(aCx, aObj, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } runnable->Dispatch(aRv); if (aRv.Failed()) { return nullptr; } if (NS_FAILED(runnable->ErrorCode())) { aRv.Throw(runnable->ErrorCode()); return nullptr; } return promise.forget(); }
/* static */ already_AddRefed<FileReader> FileReader::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv) { // The owner can be null when this object is used by chrome code. nsCOMPtr<nsPIDOMWindow> owner = do_QueryInterface(aGlobal.GetAsSupports()); WorkerPrivate* workerPrivate = nullptr; if (!NS_IsMainThread()) { JSContext* cx = aGlobal.Context(); workerPrivate = GetWorkerPrivateFromContext(cx); MOZ_ASSERT(workerPrivate); } RefPtr<FileReader> fileReader = new FileReader(owner, workerPrivate); if (!owner && nsContentUtils::ThreadsafeIsCallerChrome()) { // Instead of grabbing some random global from the context stack, // let's use the default one (junk scope) for now. // We should move away from this Init... fileReader->BindToOwner(xpc::NativeGlobal(xpc::PrivilegedJunkScope())); } return fileReader.forget(); }
bool Load(JSContext* aCx, uintN aURLCount, jsval* aURLs) { WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx); NS_ASSERTION(worker, "This should never be null!"); if (!aURLCount) { return true; } if (aURLCount > MAX_CONCURRENT_SCRIPTS) { JS_ReportError(aCx, "Cannot load more than %d scripts at one time!", MAX_CONCURRENT_SCRIPTS); return false; } nsTArray<ScriptLoadInfo> loadInfos; loadInfos.SetLength(PRUint32(aURLCount)); for (uintN index = 0; index < aURLCount; index++) { JSString* str = JS_ValueToString(aCx, aURLs[index]); if (!str) { return false; } size_t length; const jschar* buffer = JS_GetStringCharsAndLength(aCx, str, &length); if (!buffer) { return false; } loadInfos[index].mURL.Assign(buffer, length); } return LoadAllScripts(aCx, worker, loadInfos, false); }
already_AddRefed<Promise> WorkerDataStore::Get(JSContext* aCx, const Sequence<OwningStringOrUnsignedLong>& aId, ErrorResult& aRv) { WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx); MOZ_ASSERT(workerPrivate); workerPrivate->AssertIsOnWorkerThread(); RefPtr<Promise> promise = Promise::Create(workerPrivate->GlobalScope(), aRv); if (aRv.Failed()) { return nullptr; } RefPtr<DataStoreGetRunnable> runnable = new DataStoreGetRunnable(workerPrivate, mBackingStore, promise); if (!runnable->Id().AppendElements(aId, fallible)) { aRv.Throw(NS_ERROR_OUT_OF_MEMORY); return nullptr; } runnable->Dispatch(aRv); if (aRv.Failed()) { return nullptr; } if (runnable->Failed()) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; } return promise.forget(); }
already_AddRefed<Promise> WorkerDataStore::Clear(JSContext* aCx, const nsAString& aRevisionId, ErrorResult& aRv) { WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx); MOZ_ASSERT(workerPrivate); workerPrivate->AssertIsOnWorkerThread(); nsRefPtr<Promise> promise = Promise::Create(workerPrivate->GlobalScope(), aRv); if (aRv.Failed()) { return nullptr; } nsRefPtr<DataStoreClearRunnable> runnable = new DataStoreClearRunnable(workerPrivate, mBackingStore, promise, aRevisionId, aRv); runnable->Dispatch(aCx); return promise.forget(); }
/* static */ already_AddRefed<BroadcastChannel> BroadcastChannel::Constructor(const GlobalObject& aGlobal, const nsAString& aChannel, ErrorResult& aRv) { nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports()); // Window is null in workers. RefPtr<BroadcastChannel> bc = new BroadcastChannel(window, aChannel); nsAutoCString origin; PrincipalInfo principalInfo; if (NS_IsMainThread()) { nsCOMPtr<nsIGlobalObject> incumbent = mozilla::dom::GetIncumbentGlobal(); if (!incumbent) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; } nsIPrincipal* principal = incumbent->PrincipalOrNull(); if (!principal) { aRv.Throw(NS_ERROR_UNEXPECTED); return nullptr; } aRv = principal->GetOrigin(origin); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } aRv = PrincipalToPrincipalInfo(principal, &principalInfo); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } } else { JSContext* cx = aGlobal.Context(); WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx); MOZ_ASSERT(workerPrivate); RefPtr<StrongWorkerRef> workerRef = StrongWorkerRef::Create(workerPrivate, "BroadcastChannel", [bc] () { bc->Shutdown(); }); // We are already shutting down the worker. Let's return a non-active // object. if (NS_WARN_IF(!workerRef)) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; } RefPtr<ThreadSafeWorkerRef> tsr = new ThreadSafeWorkerRef(workerRef); RefPtr<InitializeRunnable> runnable = new InitializeRunnable(tsr, origin, principalInfo, aRv); runnable->Dispatch(Canceling, aRv); if (aRv.Failed()) { return nullptr; } bc->mWorkerRef = Move(workerRef); } // Register this component to PBackground. PBackgroundChild* actorChild = BackgroundChild::GetOrCreateForCurrentThread(); if (NS_WARN_IF(!actorChild)) { // Firefox is probably shutting down. Let's return a 'generic' error. aRv.Throw(NS_ERROR_FAILURE); return nullptr; } PBroadcastChannelChild* actor = actorChild->SendPBroadcastChannelConstructor(principalInfo, origin, nsString(aChannel)); bc->mActor = static_cast<BroadcastChannelChild*>(actor); MOZ_ASSERT(bc->mActor); bc->mActor->SetParent(bc); return bc.forget(); }
/* static */ already_AddRefed<BroadcastChannel> BroadcastChannel::Constructor(const GlobalObject& aGlobal, const nsAString& aChannel, ErrorResult& aRv) { nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports()); // Window is null in workers. nsAutoCString origin; PrincipalInfo principalInfo; WorkerPrivate* workerPrivate = nullptr; if (NS_IsMainThread()) { nsCOMPtr<nsIGlobalObject> incumbent = mozilla::dom::GetIncumbentGlobal(); if (!incumbent) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; } nsIPrincipal* principal = incumbent->PrincipalOrNull(); if (!principal) { aRv.Throw(NS_ERROR_UNEXPECTED); return nullptr; } aRv = principal->GetOrigin(origin); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } aRv = PrincipalToPrincipalInfo(principal, &principalInfo); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } } else { JSContext* cx = aGlobal.Context(); workerPrivate = GetWorkerPrivateFromContext(cx); MOZ_ASSERT(workerPrivate); RefPtr<InitializeRunnable> runnable = new InitializeRunnable(workerPrivate, origin, principalInfo, aRv); runnable->Dispatch(Closing, aRv); } if (aRv.Failed()) { return nullptr; } RefPtr<BroadcastChannel> bc = new BroadcastChannel(window, principalInfo, origin, aChannel); // Register this component to PBackground. PBackgroundChild* actor = BackgroundChild::GetForCurrentThread(); if (actor) { bc->ActorCreated(actor); } else { BackgroundChild::GetOrCreateForCurrentThread(bc); } if (!workerPrivate) { MOZ_ASSERT(window); MOZ_ASSERT(window->IsInnerWindow()); bc->mInnerID = window->WindowID(); // Register as observer for inner-window-destroyed. nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); if (obs) { obs->AddObserver(bc, "inner-window-destroyed", false); } } else { bc->mWorkerHolder = new BroadcastChannelWorkerHolder(bc); if (NS_WARN_IF(!bc->mWorkerHolder->HoldWorker(workerPrivate, Closing))) { bc->mWorkerHolder = nullptr; aRv.Throw(NS_ERROR_FAILURE); return nullptr; } } return bc.forget(); }
/* static */ already_AddRefed<BroadcastChannel> BroadcastChannel::Constructor(const GlobalObject& aGlobal, const nsAString& aChannel, ErrorResult& aRv) { nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports()); // Window is null in workers. nsAutoString origin; PrincipalInfo principalInfo; bool privateBrowsing = false; WorkerPrivate* workerPrivate = nullptr; if (NS_IsMainThread()) { nsCOMPtr<nsIGlobalObject> incumbent = mozilla::dom::GetIncumbentGlobal(); if (!incumbent) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; } nsIPrincipal* principal = incumbent->PrincipalOrNull(); if (!principal) { aRv.Throw(NS_ERROR_UNEXPECTED); return nullptr; } bool isNullPrincipal; aRv = principal->GetIsNullPrincipal(&isNullPrincipal); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } if (NS_WARN_IF(isNullPrincipal)) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; } GetOrigin(principal, origin, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } aRv = PrincipalToPrincipalInfo(principal, &principalInfo); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } nsIDocument* doc = window->GetExtantDoc(); if (doc) { privateBrowsing = nsContentUtils::IsInPrivateBrowsing(doc); // No bfcache when BroadcastChannel is used. doc->DisallowBFCaching(); } } else { JSContext* cx = aGlobal.Context(); workerPrivate = GetWorkerPrivateFromContext(cx); MOZ_ASSERT(workerPrivate); nsRefPtr<InitializeRunnable> runnable = new InitializeRunnable(workerPrivate, origin, principalInfo, privateBrowsing, aRv); runnable->Dispatch(cx); } if (aRv.Failed()) { return nullptr; } nsRefPtr<BroadcastChannel> bc = new BroadcastChannel(window, principalInfo, origin, aChannel, privateBrowsing); // Register this component to PBackground. PBackgroundChild* actor = BackgroundChild::GetForCurrentThread(); if (actor) { bc->ActorCreated(actor); } else { BackgroundChild::GetOrCreateForCurrentThread(bc); } if (!workerPrivate) { MOZ_ASSERT(window); MOZ_ASSERT(window->IsInnerWindow()); bc->mInnerID = window->WindowID(); // Register as observer for inner-window-destroyed. nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); if (obs) { obs->AddObserver(bc, "inner-window-destroyed", false); } } else { bc->mWorkerFeature = new BroadcastChannelFeature(bc); JSContext* cx = workerPrivate->GetJSContext(); if (NS_WARN_IF(!workerPrivate->AddFeature(cx, bc->mWorkerFeature))) { NS_WARNING("Failed to register the BroadcastChannel worker feature."); bc->mWorkerFeature = nullptr; aRv.Throw(NS_ERROR_FAILURE); return nullptr; } } return bc.forget(); }
bool WorkerGlobalScope::IsInAutomation(JSContext* aCx, JSObject* /* unused */) { return GetWorkerPrivateFromContext(aCx)->IsInAutomation(); }