Backed out 24 changesets (bug 1616353) for fission assertion failures nsGlobalWindowOuter.cpp.

Backed out changeset 3a43210e4900 (bug 1616353)
Backed out changeset cb77e9149cf8 (bug 1616353)
Backed out changeset 3aacc7cfe33f (bug 1616353)
Backed out changeset c026b06063a5 (bug 1616353)
Backed out changeset 580e790c5d17 (bug 1616353)
Backed out changeset 6f09bc1c476d (bug 1616353)
Backed out changeset 6955906262c0 (bug 1616353)
Backed out changeset a7700472807a (bug 1616353)
Backed out changeset a4735096e01b (bug 1616353)
Backed out changeset bd1706c57d91 (bug 1616353)
Backed out changeset 363c13296fda (bug 1616353)
Backed out changeset e414df387524 (bug 1616353)
Backed out changeset 765d3364cca0 (bug 1616353)
Backed out changeset 8a13355b4ac4 (bug 1616353)
Backed out changeset ada17fb8fca7 (bug 1616353)
Backed out changeset 6b6b99af186d (bug 1616353)
Backed out changeset ea966e78b296 (bug 1616353)
Backed out changeset cb88e0bbb3b9 (bug 1616353)
Backed out changeset f89a89015114 (bug 1616353)
Backed out changeset ae6058552969 (bug 1616353)
Backed out changeset f42bb5b48c1b (bug 1616353)
Backed out changeset 1ab9d22c73bb (bug 1616353)
Backed out changeset 2692c2c1396b (bug 1616353)
Backed out changeset 11a279c8da08 (bug 1616353)
This commit is contained in:
Cosmin Sabou
2020-04-06 20:03:02 +03:00
parent 832d367dd8
commit 357a0887a1
128 changed files with 3330 additions and 1323 deletions

View File

@@ -360,6 +360,7 @@ nsDocShell::nsDocShell(BrowsingContext* aBrowsingContext,
mDefaultLoadFlags(nsIRequest::LOAD_NORMAL),
mFailedLoadType(0),
mFrameType(FRAME_TYPE_REGULAR),
mPrivateBrowsingId(0),
mDisplayMode(nsIDocShell::DISPLAY_MODE_BROWSER),
mJSRunToCompletionDepth(0),
mTouchEventsOverride(nsIDocShell::TOUCHEVENTS_OVERRIDE_NONE),
@@ -386,9 +387,13 @@ nsDocShell::nsDocShell(BrowsingContext* aBrowsingContext,
mDisableMetaRefreshWhenInactive(false),
mIsAppTab(false),
mUseGlobalHistory(false),
mUseRemoteTabs(false),
mUseRemoteSubframes(false),
mUseTrackingProtection(false),
mDeviceSizeIsPageSize(false),
mWindowDraggingAllowed(false),
mInFrameSwap(false),
mInheritPrivateBrowsingId(true),
mCanExecuteScripts(false),
mFiredUnloadEvent(false),
mEODForCurrentDocument(false),
@@ -407,6 +412,8 @@ nsDocShell::nsDocShell(BrowsingContext* aBrowsingContext,
mWillChangeProcess(false),
mWatchedByDevtools(false),
mIsNavigating(false) {
AssertOriginAttributesMatchPrivateBrowsing();
// If no outer window ID was provided, generate a new one.
if (aContentWindowID == 0) {
mContentWindowID = nsContentUtils::GenerateWindowId();
@@ -526,12 +533,6 @@ already_AddRefed<nsDocShell> nsDocShell::Create(
return nullptr;
}
// If our BrowsingContext has private browsing enabled, update the number of
// private browsing docshells.
if (aBrowsingContext->UsePrivateBrowsing()) {
ds->NotifyPrivateBrowsingChanged();
}
// If our parent is present in this process, set up our parent now.
RefPtr<BrowsingContext> parent = aBrowsingContext->GetParent();
if (parent && parent->GetDocShell()) {
@@ -800,12 +801,10 @@ nsDocShell::LoadURI(nsDocShellLoadState* aLoadState, bool aSetNavigating) {
BrowsingContext::Type bcType = mBrowsingContext->GetType();
// Set up the inheriting principal in LoadState.
nsresult rv = aLoadState->SetupInheritingPrincipal(
bcType, mBrowsingContext->OriginAttributesRef());
nsresult rv = aLoadState->SetupInheritingPrincipal(bcType, mOriginAttributes);
NS_ENSURE_SUCCESS(rv, rv);
rv = aLoadState->SetupTriggeringPrincipal(
mBrowsingContext->OriginAttributesRef());
rv = aLoadState->SetupTriggeringPrincipal(mOriginAttributes);
NS_ENSURE_SUCCESS(rv, rv);
aLoadState->CalculateLoadURIFlags();
@@ -1525,40 +1524,66 @@ nsDocShell::SetAllowJavascript(bool aAllowJavascript) {
NS_IMETHODIMP
nsDocShell::GetUsePrivateBrowsing(bool* aUsePrivateBrowsing) {
NS_ENSURE_ARG_POINTER(aUsePrivateBrowsing);
return mBrowsingContext->GetUsePrivateBrowsing(aUsePrivateBrowsing);
}
void nsDocShell::NotifyPrivateBrowsingChanged() {
MOZ_ASSERT(!mIsBeingDestroyed);
if (mAffectPrivateSessionLifetime) {
if (UsePrivateBrowsing()) {
IncreasePrivateDocShellCount();
} else {
DecreasePrivateDocShellCount();
}
}
nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mPrivacyObservers);
while (iter.HasMore()) {
nsWeakPtr ref = iter.GetNext();
nsCOMPtr<nsIPrivacyTransitionObserver> obs = do_QueryReferent(ref);
if (!obs) {
mPrivacyObservers.RemoveElement(ref);
} else {
obs->PrivateModeChanged(UsePrivateBrowsing());
}
}
AssertOriginAttributesMatchPrivateBrowsing();
*aUsePrivateBrowsing = mPrivateBrowsingId > 0;
return NS_OK;
}
NS_IMETHODIMP
nsDocShell::SetUsePrivateBrowsing(bool aUsePrivateBrowsing) {
return mBrowsingContext->SetUsePrivateBrowsing(aUsePrivateBrowsing);
if (!CanSetOriginAttributes()) {
bool changed = aUsePrivateBrowsing != (mPrivateBrowsingId > 0);
return changed ? NS_ERROR_FAILURE : NS_OK;
}
return SetPrivateBrowsing(aUsePrivateBrowsing);
}
NS_IMETHODIMP
nsDocShell::SetPrivateBrowsing(bool aUsePrivateBrowsing) {
return mBrowsingContext->SetPrivateBrowsing(aUsePrivateBrowsing);
MOZ_ASSERT(!mIsBeingDestroyed);
bool changed = aUsePrivateBrowsing != (mPrivateBrowsingId > 0);
if (changed) {
mPrivateBrowsingId = aUsePrivateBrowsing ? 1 : 0;
if (mItemType != typeChrome) {
mOriginAttributes.SyncAttributesWithPrivateBrowsing(aUsePrivateBrowsing);
}
if (mAffectPrivateSessionLifetime) {
if (aUsePrivateBrowsing) {
IncreasePrivateDocShellCount();
} else {
DecreasePrivateDocShellCount();
}
}
}
nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
while (iter.HasMore()) {
nsCOMPtr<nsILoadContext> shell = do_QueryObject(iter.GetNext());
if (shell) {
shell->SetPrivateBrowsing(aUsePrivateBrowsing);
}
}
if (changed) {
nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mPrivacyObservers);
while (iter.HasMore()) {
nsWeakPtr ref = iter.GetNext();
nsCOMPtr<nsIPrivacyTransitionObserver> obs = do_QueryReferent(ref);
if (!obs) {
mPrivacyObservers.RemoveElement(ref);
} else {
obs->PrivateModeChanged(aUsePrivateBrowsing);
}
}
}
AssertOriginAttributesMatchPrivateBrowsing();
return NS_OK;
}
NS_IMETHODIMP
@@ -1572,23 +1597,52 @@ nsDocShell::GetHasLoadedNonBlankURI(bool* aResult) {
NS_IMETHODIMP
nsDocShell::GetUseRemoteTabs(bool* aUseRemoteTabs) {
NS_ENSURE_ARG_POINTER(aUseRemoteTabs);
return mBrowsingContext->GetUseRemoteTabs(aUseRemoteTabs);
*aUseRemoteTabs = mUseRemoteTabs;
return NS_OK;
}
NS_IMETHODIMP
nsDocShell::SetRemoteTabs(bool aUseRemoteTabs) {
return mBrowsingContext->SetRemoteTabs(aUseRemoteTabs);
if (aUseRemoteTabs) {
CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::DOMIPCEnabled,
true);
}
// Don't allow non-remote tabs with remote subframes.
if (NS_WARN_IF(!aUseRemoteTabs && mUseRemoteSubframes)) {
return NS_ERROR_UNEXPECTED;
}
mUseRemoteTabs = aUseRemoteTabs;
return NS_OK;
}
NS_IMETHODIMP
nsDocShell::GetUseRemoteSubframes(bool* aUseRemoteSubframes) {
NS_ENSURE_ARG_POINTER(aUseRemoteSubframes);
return mBrowsingContext->GetUseRemoteSubframes(aUseRemoteSubframes);
*aUseRemoteSubframes = mUseRemoteSubframes;
return NS_OK;
}
NS_IMETHODIMP
nsDocShell::SetRemoteSubframes(bool aUseRemoteSubframes) {
return mBrowsingContext->SetRemoteSubframes(aUseRemoteSubframes);
static bool annotated = false;
if (aUseRemoteSubframes && !annotated) {
annotated = true;
CrashReporter::AnnotateCrashReport(
CrashReporter::Annotation::DOMFissionEnabled, true);
}
// Don't allow non-remote tabs with remote subframes.
if (NS_WARN_IF(aUseRemoteSubframes && !mUseRemoteTabs)) {
return NS_ERROR_UNEXPECTED;
}
mUseRemoteSubframes = aUseRemoteSubframes;
return NS_OK;
}
NS_IMETHODIMP
@@ -1597,6 +1651,7 @@ nsDocShell::SetAffectPrivateSessionLifetime(bool aAffectLifetime) {
bool change = aAffectLifetime != mAffectPrivateSessionLifetime;
if (change && UsePrivateBrowsing()) {
AssertOriginAttributesMatchPrivateBrowsing();
if (aAffectLifetime) {
IncreasePrivateDocShellCount();
} else {
@@ -1785,6 +1840,18 @@ nsDocShell::SetAllowContentRetargetingOnChildren(
return NS_OK;
}
NS_IMETHODIMP
nsDocShell::GetInheritPrivateBrowsingId(bool* aInheritPrivateBrowsingId) {
*aInheritPrivateBrowsingId = mInheritPrivateBrowsingId;
return NS_OK;
}
NS_IMETHODIMP
nsDocShell::SetInheritPrivateBrowsingId(bool aInheritPrivateBrowsingId) {
mInheritPrivateBrowsingId = aInheritPrivateBrowsingId;
return NS_OK;
}
NS_IMETHODIMP
nsDocShell::GetFullscreenAllowed(bool* aFullscreenAllowed) {
NS_ENSURE_ARG_POINTER(aFullscreenAllowed);
@@ -2563,6 +2630,9 @@ void nsDocShell::RecomputeCanExecuteScripts() {
nsresult nsDocShell::SetDocLoaderParent(nsDocLoader* aParent) {
bool wasFrame = IsFrame();
#ifdef DEBUG
bool wasPrivate = UsePrivateBrowsing();
#endif
nsresult rv = nsDocLoader::SetDocLoaderParent(aParent);
NS_ENSURE_SUCCESS(rv, rv);
@@ -2609,8 +2679,10 @@ nsresult nsDocShell::SetDocLoaderParent(nsDocLoader* aParent) {
value = false;
}
SetAllowDNSPrefetch(mAllowDNSPrefetch && value);
SetAffectPrivateSessionLifetime(
parentAsDocShell->GetAffectPrivateSessionLifetime());
if (mInheritPrivateBrowsingId) {
value = parentAsDocShell->GetAffectPrivateSessionLifetime();
SetAffectPrivateSessionLifetime(value);
}
uint32_t flags;
if (NS_SUCCEEDED(parentAsDocShell->GetDefaultLoadFlags(&flags))) {
SetDefaultLoadFlags(flags);
@@ -2623,6 +2695,12 @@ nsresult nsDocShell::SetDocLoaderParent(nsDocLoader* aParent) {
// like this that might be embedded within it.
}
nsCOMPtr<nsILoadContext> parentAsLoadContext(do_QueryInterface(parent));
if (parentAsLoadContext && mInheritPrivateBrowsingId &&
NS_SUCCEEDED(parentAsLoadContext->GetUsePrivateBrowsing(&value))) {
SetPrivateBrowsing(value);
}
nsCOMPtr<nsIURIContentListener> parentURIListener(do_GetInterface(parent));
if (parentURIListener) {
mContentListener->SetParentContentListener(parentURIListener);
@@ -2636,6 +2714,9 @@ nsresult nsDocShell::SetDocLoaderParent(nsDocLoader* aParent) {
MaybeClearStorageAccessFlag();
}
NS_ASSERTION(mInheritPrivateBrowsingId || wasPrivate == UsePrivateBrowsing(),
"Private browsing state changed while inheritance was disabled");
return NS_OK;
}
@@ -2749,6 +2830,18 @@ nsDocShell::GetSameTypeRootTreeItemIgnoreBrowserBoundaries(
return NS_OK;
}
void nsDocShell::AssertOriginAttributesMatchPrivateBrowsing() {
// Chrome docshells must not have a private browsing OriginAttribute
// Content docshells must maintain the equality:
// mOriginAttributes.mPrivateBrowsingId == mPrivateBrowsingId
if (mItemType == typeChrome) {
MOZ_DIAGNOSTIC_ASSERT(mOriginAttributes.mPrivateBrowsingId == 0);
} else {
MOZ_DIAGNOSTIC_ASSERT(mOriginAttributes.mPrivateBrowsingId ==
mPrivateBrowsingId);
}
}
bool nsDocShell::IsSandboxedFrom(BrowsingContext* aTargetBC) {
// If no target then not sandboxed.
if (!aTargetBC) {
@@ -2967,6 +3060,9 @@ nsDocShell::AddChild(nsIDocShellTreeItem* aChild) {
childDocShell->SetUseGlobalHistory(true);
}
Cast(childDocShell)->SetRemoteTabs(mUseRemoteTabs);
Cast(childDocShell)->SetRemoteSubframes(mUseRemoteSubframes);
if (aChild->ItemType() != mItemType) {
return NS_OK;
}
@@ -3583,20 +3679,19 @@ nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI,
do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, aURI, flags,
GetOriginAttributes(), nullptr, nullptr,
&isStsHost);
mOriginAttributes, nullptr, nullptr, &isStsHost);
NS_ENSURE_SUCCESS(rv, rv);
rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HPKP, aURI, flags,
GetOriginAttributes(), nullptr, nullptr,
mOriginAttributes, nullptr, nullptr,
&isPinnedHost);
NS_ENSURE_SUCCESS(rv, rv);
} else {
mozilla::dom::ContentChild* cc =
mozilla::dom::ContentChild::GetSingleton();
cc->SendIsSecureURI(nsISiteSecurityService::HEADER_HSTS, aURI, flags,
GetOriginAttributes(), &isStsHost);
mOriginAttributes, &isStsHost);
cc->SendIsSecureURI(nsISiteSecurityService::HEADER_HPKP, aURI, flags,
GetOriginAttributes(), &isPinnedHost);
mOriginAttributes, &isPinnedHost);
}
if (Preferences::GetBool("browser.xul.error_pages.expert_bad_cert",
@@ -4355,6 +4450,8 @@ nsDocShell::Destroy() {
NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
"Unexpected item type in docshell");
AssertOriginAttributesMatchPrivateBrowsing();
nsCOMPtr<nsIObserverService> serv = services::GetObserverService();
if (serv) {
const char* msg = mItemType == typeContent
@@ -4462,8 +4559,12 @@ nsDocShell::Destroy() {
// to break the cycle between us and the timers.
CancelRefreshURITimers();
if (UsePrivateBrowsing() && mAffectPrivateSessionLifetime) {
DecreasePrivateDocShellCount();
if (UsePrivateBrowsing()) {
mPrivateBrowsingId = nsIScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID;
mOriginAttributes.SyncAttributesWithPrivateBrowsing(false);
if (mAffectPrivateSessionLifetime) {
DecreasePrivateDocShellCount();
}
}
return NS_OK;
@@ -5000,6 +5101,7 @@ nsDocShell::SetTitle(const nsAString& aTitle) {
}
}
AssertOriginAttributesMatchPrivateBrowsing();
if (mCurrentURI && mLoadType != LOAD_ERROR_PAGE) {
UpdateGlobalHistoryTitle(mCurrentURI);
}
@@ -6400,8 +6502,7 @@ nsresult nsDocShell::CreateAboutBlankContentViewer(
if (aPrincipal && !aPrincipal->IsSystemPrincipal() &&
mItemType != typeChrome) {
MOZ_ASSERT(aPrincipal->OriginAttributesRef() ==
mBrowsingContext->OriginAttributesRef());
MOZ_ASSERT(aPrincipal->OriginAttributesRef() == mOriginAttributes);
}
// Make sure timing is created. But first record whether we had it
@@ -11854,27 +11955,63 @@ nsDocShell::GetAssociatedWindow(mozIDOMWindowProxy** aWindow) {
NS_IMETHODIMP
nsDocShell::GetTopWindow(mozIDOMWindowProxy** aWindow) {
return mBrowsingContext->GetTopWindow(aWindow);
nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
if (win) {
win = win->GetInProcessTop();
}
win.forget(aWindow);
return NS_OK;
}
NS_IMETHODIMP
nsDocShell::GetTopFrameElement(Element** aElement) {
return mBrowsingContext->GetTopFrameElement(aElement);
*aElement = nullptr;
nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
if (!win) {
return NS_OK;
}
nsCOMPtr<nsPIDOMWindowOuter> top = win->GetInProcessScriptableTop();
NS_ENSURE_TRUE(top, NS_ERROR_FAILURE);
// GetFrameElementInternal, /not/ GetScriptableFrameElement -- if |top| is
// inside <iframe mozbrowser>, we want to return the iframe, not null.
// And we want to cross the content/chrome boundary.
RefPtr<Element> elt = top->GetFrameElementInternal();
elt.forget(aElement);
return NS_OK;
}
NS_IMETHODIMP
nsDocShell::GetNestedFrameId(uint64_t* aId) {
return mBrowsingContext->GetNestedFrameId(aId);
*aId = 0;
return NS_OK;
}
NS_IMETHODIMP
nsDocShell::GetUseTrackingProtection(bool* aUseTrackingProtection) {
return mBrowsingContext->GetUseTrackingProtection(aUseTrackingProtection);
*aUseTrackingProtection = false;
if (mUseTrackingProtection ||
StaticPrefs::privacy_trackingprotection_enabled() ||
(UsePrivateBrowsing() &&
StaticPrefs::privacy_trackingprotection_pbmode_enabled())) {
*aUseTrackingProtection = true;
return NS_OK;
}
RefPtr<nsDocShell> parent = GetInProcessParentDocshell();
if (parent) {
return parent->GetUseTrackingProtection(aUseTrackingProtection);
}
return NS_OK;
}
NS_IMETHODIMP
nsDocShell::SetUseTrackingProtection(bool aUseTrackingProtection) {
return mBrowsingContext->SetUseTrackingProtection(aUseTrackingProtection);
mUseTrackingProtection = aUseTrackingProtection;
return NS_OK;
}
NS_IMETHODIMP
@@ -12510,14 +12647,42 @@ NS_IMETHODIMP nsDocShell::GetIsTopLevelContentDocShell(
NS_IMETHODIMP
nsDocShell::GetScriptableOriginAttributes(JSContext* aCx,
JS::MutableHandle<JS::Value> aVal) {
return mBrowsingContext->GetScriptableOriginAttributes(aCx, aVal);
return GetOriginAttributes(aCx, aVal);
}
// Implements nsIDocShell.GetOriginAttributes()
NS_IMETHODIMP
nsDocShell::GetOriginAttributes(JSContext* aCx,
JS::MutableHandle<JS::Value> aVal) {
return mBrowsingContext->GetScriptableOriginAttributes(aCx, aVal);
bool ok = ToJSValue(aCx, mOriginAttributes, aVal);
NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
return NS_OK;
}
bool nsDocShell::CanSetOriginAttributes() {
MOZ_ASSERT(mChildList.IsEmpty());
if (!mChildList.IsEmpty()) {
return false;
}
// TODO: Bug 1273058 - mContentViewer should be null when setting origin
// attributes.
if (mContentViewer) {
Document* doc = mContentViewer->GetDocument();
if (doc) {
nsIURI* uri = doc->GetDocumentURI();
if (!uri) {
return false;
}
nsCString uriSpec = uri->GetSpecOrDefault();
MOZ_ASSERT(uriSpec.EqualsLiteral("about:blank"));
if (!uriSpec.EqualsLiteral("about:blank")) {
return false;
}
}
}
return true;
}
bool nsDocShell::ServiceWorkerAllowedToControlWindow(nsIPrincipal* aPrincipal,
@@ -12543,7 +12708,41 @@ bool nsDocShell::ServiceWorkerAllowedToControlWindow(nsIPrincipal* aPrincipal,
nsresult nsDocShell::SetOriginAttributes(const OriginAttributes& aAttrs) {
MOZ_ASSERT(!mIsBeingDestroyed);
return mBrowsingContext->SetOriginAttributes(aAttrs);
if (!CanSetOriginAttributes()) {
return NS_ERROR_FAILURE;
}
AssertOriginAttributesMatchPrivateBrowsing();
mOriginAttributes = aAttrs;
bool isPrivate = mOriginAttributes.mPrivateBrowsingId !=
nsIScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID;
// Chrome docshell can not contain OriginAttributes.mPrivateBrowsingId
if (mItemType == typeChrome && isPrivate) {
mOriginAttributes.mPrivateBrowsingId =
nsIScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID;
}
SetPrivateBrowsing(isPrivate);
AssertOriginAttributesMatchPrivateBrowsing();
return NS_OK;
}
NS_IMETHODIMP
nsDocShell::SetOriginAttributesBeforeLoading(
JS::Handle<JS::Value> aOriginAttributes, JSContext* aCx) {
if (!aOriginAttributes.isObject()) {
return NS_ERROR_INVALID_ARG;
}
OriginAttributes attrs;
if (!attrs.Init(aCx, aOriginAttributes)) {
return NS_ERROR_INVALID_ARG;
}
return SetOriginAttributes(attrs);
}
NS_IMETHODIMP
@@ -12682,6 +12881,15 @@ bool nsDocShell::IsInvisible() { return mInvisible; }
void nsDocShell::SetInvisible(bool aInvisible) { mInvisible = aInvisible; }
void nsDocShell::SetOpener(nsIRemoteTab* aOpener) {
mOpener = do_GetWeakReference(aOpener);
}
nsIRemoteTab* nsDocShell::GetOpener() {
nsCOMPtr<nsIRemoteTab> opener(do_QueryReferent(mOpener));
return opener;
}
// The caller owns |aAsyncCause| here.
void nsDocShell::NotifyJSRunToCompletionStart(const char* aReason,
const nsAString& aFunctionName,
@@ -12826,7 +13034,7 @@ nsDocShell::GetAwaitingLargeAlloc(bool* aResult) {
NS_IMETHODIMP_(void)
nsDocShell::GetOriginAttributes(mozilla::OriginAttributes& aAttrs) {
mBrowsingContext->GetOriginAttributes(aAttrs);
aAttrs = mOriginAttributes;
}
HTMLEditor* nsIDocShell::GetHTMLEditor() {