Bug 1966586 - Reuse other browser windows when opening _blank links in Taskbar Tabs windows. r=nrishel
This doesn't affect other tab additions, nor does it stop the tab bar
from appearing altogether. The idea is that _if_ another tab is somehow
made, the user should see it; but we should not create new tabs if we
can avoid it.
This also adds tests for opening URIs in popups and taskbar tabs to make
it less likely that this breaks in future.
Differential Revision: https://phabricator.services.mozilla.com/D253726
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- *//* vim: set ts=8 sts=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/. */#include"FontFaceSet.h"#include"FontPreloader.h"#include"ReferrerInfo.h"#include"gfxFontConstants.h"#include"gfxFontSrcPrincipal.h"#include"gfxFontSrcURI.h"#include"gfxFontUtils.h"#include"mozilla/AsyncEventDispatcher.h"#include"mozilla/BasePrincipal.h"#include"mozilla/FontPropertyTypes.h"#include"mozilla/LoadInfo.h"#include"mozilla/Logging.h"#include"mozilla/Preferences.h"#include"mozilla/PresShell.h"#include"mozilla/PresShellInlines.h"#include"mozilla/ServoBindings.h"#include"mozilla/ServoCSSParser.h"#include"mozilla/ServoStyleSet.h"#include"mozilla/ServoUtils.h"#include"mozilla/Sprintf.h"#include"mozilla/css/Loader.h"#include"mozilla/dom/CSSFontFaceRule.h"#include"mozilla/dom/Document.h"#include"mozilla/dom/DocumentInlines.h"#include"mozilla/dom/Event.h"#include"mozilla/dom/FontFaceImpl.h"#include"mozilla/dom/FontFaceSetBinding.h"#include"mozilla/dom/FontFaceSetDocumentImpl.h"#include"mozilla/dom/FontFaceSetIterator.h"#include"mozilla/dom/FontFaceSetLoadEvent.h"#include"mozilla/dom/FontFaceSetLoadEventBinding.h"#include"mozilla/dom/FontFaceSetWorkerImpl.h"#include"mozilla/dom/Promise.h"#include"nsComponentManagerUtils.h"#include"nsContentPolicyUtils.h"#include"nsContentUtils.h"#include"nsDOMNavigationTiming.h"#include"nsDeviceContext.h"#include"nsFontFaceLoader.h"#include"nsIConsoleService.h"#include"nsIContentPolicy.h"#include"nsIDocShell.h"#include"nsIInputStream.h"#include"nsILoadContext.h"#include"nsINetworkPredictor.h"#include"nsIPrincipal.h"#include"nsIWebNavigation.h"#include"nsLayoutUtils.h"#include"nsNetUtil.h"#include"nsPresContext.h"#include"nsPrintfCString.h"#include"nsUTF8Utils.h"usingnamespacemozilla;usingnamespacemozilla::css;usingnamespacemozilla::dom;NS_IMPL_CYCLE_COLLECTION_CLASS(FontFaceSet)NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(FontFaceSet,DOMEventTargetHelper)NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mImpl->GetDocument());NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReady);for(size_ti=0;i<tmp->mRuleFaces.Length();i++){NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRuleFaces[i].mFontFace);}for(size_ti=0;i<tmp->mNonRuleFaces.Length();i++){NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNonRuleFaces[i].mFontFace);}NS_IMPL_CYCLE_COLLECTION_TRAVERSE_ENDNS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FontFaceSet,DOMEventTargetHelper)tmp->Destroy();NS_IMPL_CYCLE_COLLECTION_UNLINK(mReady);for(size_ti=0;i<tmp->mRuleFaces.Length();i++){NS_IMPL_CYCLE_COLLECTION_UNLINK(mRuleFaces[i].mFontFace);}for(size_ti=0;i<tmp->mNonRuleFaces.Length();i++){NS_IMPL_CYCLE_COLLECTION_UNLINK(mNonRuleFaces[i].mFontFace);}NS_IMPL_CYCLE_COLLECTION_UNLINK_ENDNS_IMPL_ADDREF_INHERITED(FontFaceSet,DOMEventTargetHelper)NS_IMPL_RELEASE_INHERITED(FontFaceSet,DOMEventTargetHelper)NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FontFaceSet)NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)FontFaceSet::FontFaceSet(nsIGlobalObject*aParent):DOMEventTargetHelper(aParent){}FontFaceSet::~FontFaceSet(){// Assert that we don't drop any FontFaceSet objects during a Servo traversal,// since PostTraversalTask objects can hold raw pointers to FontFaceSets.MOZ_ASSERT(!gfxFontUtils::IsInServoTraversal());Destroy();}/* static */already_AddRefed<FontFaceSet>FontFaceSet::CreateForDocument(dom::Document*aDocument){RefPtr<FontFaceSet>set=newFontFaceSet(aDocument->GetScopeObject());RefPtr<FontFaceSetDocumentImpl>impl=newFontFaceSetDocumentImpl(set,aDocument);set->mImpl=impl;impl->Initialize();returnset.forget();}/* static */already_AddRefed<FontFaceSet>FontFaceSet::CreateForWorker(nsIGlobalObject*aParent,WorkerPrivate*aWorkerPrivate){RefPtr<FontFaceSet>set=newFontFaceSet(aParent);RefPtr<FontFaceSetWorkerImpl>impl=newFontFaceSetWorkerImpl(set);set->mImpl=impl;if(NS_WARN_IF(!impl->Initialize(aWorkerPrivate))){returnnullptr;}returnset.forget();}JSObject*FontFaceSet::WrapObject(JSContext*aContext,JS::Handle<JSObject*>aGivenProto){returnFontFaceSet_Binding::Wrap(aContext,this,aGivenProto);}voidFontFaceSet::Destroy(){mImpl->Destroy();}already_AddRefed<Promise>FontFaceSet::Load(JSContext*aCx,constnsACString&aFont,constnsAString&aText,ErrorResult&aRv){FlushUserFontSet();nsTArray<RefPtr<Promise>>promises;nsTArray<RefPtr<FontFace>>faces;{nsTArray<FontFace*>weakFaces;mImpl->FindMatchingFontFaces(aFont,aText,weakFaces,aRv);if(aRv.Failed()){returnnullptr;}if(!faces.AppendElements(weakFaces,fallible)||!promises.SetCapacity(weakFaces.Length(),fallible)){aRv.Throw(NS_ERROR_FAILURE);returnnullptr;}}for(FontFace*f:faces){RefPtr<Promise>promise=f->Load(aRv);if(aRv.Failed()){returnnullptr;}promises.AppendElement(promise);}returnPromise::All(aCx,promises,aRv);}boolFontFaceSet::Check(constnsACString&aFont,constnsAString&aText,ErrorResult&aRv){FlushUserFontSet();nsTArray<FontFace*>faces;mImpl->FindMatchingFontFaces(aFont,aText,faces,aRv);if(aRv.Failed()){returnfalse;}for(FontFace*f:faces){if(f->Status()!=FontFaceLoadStatus::Loaded){returnfalse;}}returntrue;}boolFontFaceSet::ReadyPromiseIsPending()const{returnmReady?mReady->State()==Promise::PromiseState::Pending:!mResolveLazilyCreatedReadyPromise;}Promise*FontFaceSet::GetReady(ErrorResult&aRv){mImpl->EnsureReady();if(!mReady){nsCOMPtr<nsIGlobalObject>global=GetParentObject();mReady=Promise::Create(global,aRv);if(!mReady){aRv.Throw(NS_ERROR_FAILURE);returnnullptr;}if(mResolveLazilyCreatedReadyPromise){mReady->MaybeResolve(this);mResolveLazilyCreatedReadyPromise=false;}}returnmReady;}FontFaceSetLoadStatusFontFaceSet::Status(){returnmImpl->Status();}#ifdef DEBUGboolFontFaceSet::HasRuleFontFace(FontFace*aFontFace){for(size_ti=0;i<mRuleFaces.Length();i++){if(mRuleFaces[i].mFontFace==aFontFace){returntrue;}}returnfalse;}#endifvoidFontFaceSet::Add(FontFace&aFontFace,ErrorResult&aRv){FlushUserFontSet();FontFaceImpl*fontImpl=aFontFace.GetImpl();MOZ_ASSERT(fontImpl);if(!mImpl->Add(fontImpl,aRv)){return;}MOZ_ASSERT(!aRv.Failed());#ifdef DEBUGfor(constFontFaceRecord&rec:mNonRuleFaces){MOZ_ASSERT(rec.mFontFace!=&aFontFace,"FontFace should not occur in mNonRuleFaces twice");}#endifFontFaceRecord*rec=mNonRuleFaces.AppendElement();rec->mFontFace=&aFontFace;rec->mOrigin=Nothing();rec->mLoadEventShouldFire=fontImpl->Status()==FontFaceLoadStatus::Unloaded||fontImpl->Status()==FontFaceLoadStatus::Loading;}voidFontFaceSet::Clear(){nsTArray<FontFaceRecord>oldRecords=std::move(mNonRuleFaces);mImpl->Clear();}boolFontFaceSet::Delete(FontFace&aFontFace){// Hold onto a strong reference to make sure that when we remove FontFace from// the list, the FontFaceImpl does not get freed right away. We need to check// the FontFaceSetImpl first.RefPtr<FontFaceImpl>fontImpl=aFontFace.GetImpl();MOZ_ASSERT(fontImpl);// Ensure that we remove from mNonRuleFaces first. This is important so that// when we check to see if all of the fonts have finished loading, the list in// FontFaceSet and FontFaceSetImpl match.boolremoved=false;for(size_ti=0;i<mNonRuleFaces.Length();i++){if(mNonRuleFaces[i].mFontFace==&aFontFace){mNonRuleFaces.RemoveElementAt(i);removed=true;break;}}if(!mImpl->Delete(fontImpl)){MOZ_ASSERT(!removed,"Missing rule present in Impl!");}else{MOZ_ASSERT(removed,"Rule present but missing in Impl!");}returnremoved;}boolFontFaceSet::HasAvailableFontFace(FontFace*aFontFace){returnaFontFace->GetImpl()->IsInFontFaceSet(mImpl);}boolFontFaceSet::Has(FontFace&aFontFace){FlushUserFontSet();returnHasAvailableFontFace(&aFontFace);}FontFace*FontFaceSet::GetFontFaceAt(uint32_taIndex){FlushUserFontSet();if(aIndex<mRuleFaces.Length()){auto&entry=mRuleFaces[aIndex];if(entry.mOrigin.value()!=StyleOrigin::Author){returnnullptr;}returnentry.mFontFace;}aIndex-=mRuleFaces.Length();if(aIndex<mNonRuleFaces.Length()){returnmNonRuleFaces[aIndex].mFontFace;}returnnullptr;}uint32_tFontFaceSet::Size(){FlushUserFontSet();// Web IDL objects can only expose array index properties up to INT32_MAX.size_ttotal=mNonRuleFaces.Length();for(constauto&entry:mRuleFaces){if(entry.mOrigin.value()==StyleOrigin::Author){++total;}}returnstd::min<size_t>(total,INT32_MAX);}uint32_tFontFaceSet::SizeIncludingNonAuthorOrigins(){FlushUserFontSet();// Web IDL objects can only expose array index properties up to INT32_MAX.size_ttotal=mRuleFaces.Length()+mNonRuleFaces.Length();returnstd::min<size_t>(total,INT32_MAX);}already_AddRefed<FontFaceSetIterator>FontFaceSet::Entries(){RefPtr<FontFaceSetIterator>it=newFontFaceSetIterator(this,true);returnit.forget();}already_AddRefed<FontFaceSetIterator>FontFaceSet::Values(){RefPtr<FontFaceSetIterator>it=newFontFaceSetIterator(this,false);returnit.forget();}voidFontFaceSet::ForEach(JSContext*aCx,FontFaceSetForEachCallback&aCallback,JS::Handle<JS::Value>aThisArg,ErrorResult&aRv){JS::Rooted<JS::Value>thisArg(aCx,aThisArg);for(size_ti=0;i<SizeIncludingNonAuthorOrigins();i++){RefPtr<FontFace>face=GetFontFaceAt(i);if(!face){// The font at index |i| is a non-Author origin font, which we shouldn't// expose per spec.continue;}aCallback.Call(thisArg,*face,*face,*this,aRv);if(aRv.Failed()){return;}}}boolFontFaceSet::UpdateRules(constnsTArray<nsFontFaceRuleContainer>&aRules){// The impl object handles the callbacks for recreating the mRulesFaces array.nsTArray<FontFaceRecord>oldRecords=std::move(mRuleFaces);returnmImpl->UpdateRules(aRules);}voidFontFaceSet::InsertRuleFontFace(FontFace*aFontFace,StyleOriginaOrigin){MOZ_ASSERT(!HasRuleFontFace(aFontFace));FontFaceRecord*rec=mRuleFaces.AppendElement();rec->mFontFace=aFontFace;rec->mOrigin=Some(aOrigin);rec->mLoadEventShouldFire=aFontFace->Status()==FontFaceLoadStatus::Unloaded||aFontFace->Status()==FontFaceLoadStatus::Loading;}voidFontFaceSet::DidRefresh(){mImpl->CheckLoadingFinished();}voidFontFaceSet::DispatchLoadingEventAndReplaceReadyPromise(){gfxFontUtils::AssertSafeThreadOrServoFontMetricsLocked();if(ServoStyleSet*set=gfxFontUtils::CurrentServoStyleSet()){// See comments in Gecko_GetFontMetrics.//// We can't just dispatch the runnable below if we're not on the main// thread, since it needs to take a strong reference to the FontFaceSet,// and being a DOM object, FontFaceSet doesn't support thread-safe// refcounting. (Also, the Promise object creation must be done on// the main thread.)set->AppendTask(PostTraversalTask::DispatchLoadingEventAndReplaceReadyPromise(this));return;}(newAsyncEventDispatcher(this,u"loading"_ns,CanBubble::eNo))->PostDOMEvent();if(mReady&&mReady->State()!=Promise::PromiseState::Pending){if(GetParentObject()){ErrorResultrv;mReady=Promise::Create(GetParentObject(),rv);}}// We may previously have been in a state where all fonts had finished// loading and we'd set mResolveLazilyCreatedReadyPromise to make sure that// if we lazily create mReady for a consumer that we resolve it before// returning it. We're now loading fonts, so we need to clear that flag.mResolveLazilyCreatedReadyPromise=false;}voidFontFaceSet::MaybeResolve(){if(mReady){mReady->MaybeResolve(this);}else{mResolveLazilyCreatedReadyPromise=true;}// Now dispatch the loadingdone/loadingerror events.nsTArray<OwningNonNull<FontFace>>loaded;nsTArray<OwningNonNull<FontFace>>failed;autocheckStatus=[&](nsTArray<FontFaceRecord>&faces)->void{for(auto&face:faces){if(!face.mLoadEventShouldFire){continue;}FontFace*f=face.mFontFace;switch(f->Status()){caseFontFaceLoadStatus::Unloaded:break;caseFontFaceLoadStatus::Loaded:loaded.AppendElement(*f);face.mLoadEventShouldFire=false;break;caseFontFaceLoadStatus::Error:failed.AppendElement(*f);face.mLoadEventShouldFire=false;break;caseFontFaceLoadStatus::Loading:// We should've returned above at MightHavePendingFontLoads()!MOZ_ASSERT_UNREACHABLE("unexpected FontFaceLoadStatus");break;}}};checkStatus(mRuleFaces);checkStatus(mNonRuleFaces);DispatchLoadingFinishedEvent(u"loadingdone"_ns,std::move(loaded));if(!failed.IsEmpty()){DispatchLoadingFinishedEvent(u"loadingerror"_ns,std::move(failed));}}voidFontFaceSet::DispatchLoadingFinishedEvent(constnsAString&aType,nsTArray<OwningNonNull<FontFace>>&&aFontFaces){FontFaceSetLoadEventInitinit;init.mBubbles=false;init.mCancelable=false;init.mFontfaces=std::move(aFontFaces);RefPtr<FontFaceSetLoadEvent>event=FontFaceSetLoadEvent::Constructor(this,aType,init);(newAsyncEventDispatcher(this,event.forget()))->PostDOMEvent();}voidFontFaceSet::FlushUserFontSet(){mImpl->FlushUserFontSet();}voidFontFaceSet::RefreshStandardFontLoadPrincipal(){MOZ_ASSERT(NS_IsMainThread());mImpl->RefreshStandardFontLoadPrincipal();}voidFontFaceSet::CopyNonRuleFacesTo(FontFaceSet*aFontFaceSet)const{for(constFontFaceRecord&rec:mNonRuleFaces){IgnoredErrorResultrv;RefPtr<FontFace>f=rec.mFontFace;aFontFaceSet->Add(*f,rv);MOZ_ASSERT(!rv.Failed());}}#undef LOG_ENABLED#undef LOG