Files
sousa-gecko/dom/base/SpeculationRules.cpp
T

146 lines
4.9 KiB
C++

/* 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 https://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/SpeculationRules.h"
#include "mozilla/CycleCollectedJSContext.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/PrefetchCandidates.h"
#include "mozilla/dom/ReferrerPolicyBinding.h"
#include "mozilla/dom/SpeculationRuleSet.h"
#include "mozilla/dom/speculationrules_ffi_generated.h"
#include "nsCycleCollectionParticipant.h"
#include "nsIScriptElement.h"
#include "nsIURI.h"
namespace mozilla::dom {
#define STATIC_ASSERT_REFERRER_POLICY_EQ(cpp_, rust_) \
static_assert( \
ReferrerPolicy::cpp_ == \
static_cast<ReferrerPolicy>(SpeculationRulesReferrerPolicy::rust_))
STATIC_ASSERT_REFERRER_POLICY_EQ(_empty, Empty);
STATIC_ASSERT_REFERRER_POLICY_EQ(No_referrer, NoReferrer);
STATIC_ASSERT_REFERRER_POLICY_EQ(No_referrer_when_downgrade,
NoReferrerWhenDowngrade);
STATIC_ASSERT_REFERRER_POLICY_EQ(Origin, Origin);
STATIC_ASSERT_REFERRER_POLICY_EQ(Origin_when_cross_origin,
OriginWhenCrossOrigin);
STATIC_ASSERT_REFERRER_POLICY_EQ(Unsafe_url, UnsafeUrl);
STATIC_ASSERT_REFERRER_POLICY_EQ(Same_origin, SameOrigin);
STATIC_ASSERT_REFERRER_POLICY_EQ(Strict_origin, StrictOrigin);
STATIC_ASSERT_REFERRER_POLICY_EQ(Strict_origin_when_cross_origin,
StrictOriginWhenCrossOrigin);
#undef STATIC_ASSERT_REFERRER_POLICY_EQ
NS_IMPL_CYCLE_COLLECTION_CLASS(SpeculationRules)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(SpeculationRules)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
for (const auto& entry : tmp->mRuleSetsFromScript) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mRuleSetsFromScript key");
cb.NoteXPCOMChild(entry.GetKey());
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SpeculationRules)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mRuleSetsFromScript)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
SpeculationRules::SpeculationRules(Document* aDocument)
: mDocument(aDocument) {}
// https://html.spec.whatwg.org/#register-speculation-rules
void SpeculationRules::RegisterFromScript(
nsIScriptElement* aScriptElement, UniquePtr<SpeculationRuleSet> aRuleSet) {
// Step 2.
mRuleSetsFromScript.InsertOrUpdate(aScriptElement, std::move(aRuleSet));
// Step 3.
ConsiderLoads();
}
// html.spec.whatwg.org/#unregister-speculation-rules
void SpeculationRules::Unregister(nsIScriptElement* aScriptElement) {
// Step 2.
mRuleSetsFromScript.Remove(aScriptElement);
// Step 3.
ConsiderLoads();
}
namespace {
class ConsiderSpeculativeLoadsMicrotask final
: public mozilla::MicroTaskRunnable {
public:
explicit ConsiderSpeculativeLoadsMicrotask(
SpeculationRules* aSpeculationRules)
: mSpeculationRules(aSpeculationRules) {}
void Run(AutoSlowOperation& /* unused */) override {
mSpeculationRules->InnerConsiderLoads();
}
private:
RefPtr<SpeculationRules> mSpeculationRules;
};
} // namespace
// https://html.spec.whatwg.org/#consider-speculative-loads
void SpeculationRules::ConsiderLoads() {
// Steps 1-2.
if (!mDocument->IsTopLevelContentDocument() ||
mConsiderSpeculativeLoadsMicrotaskQueued) {
return;
}
if (CycleCollectedJSContext* context = CycleCollectedJSContext::Get()) {
// Step 3.
mConsiderSpeculativeLoadsMicrotaskQueued = true;
// Step 4.
RefPtr mt = MakeRefPtr<ConsiderSpeculativeLoadsMicrotask>(this);
context->DispatchToMicroTask(mt.forget());
}
}
// https://html.spec.whatwg.org/#inner-consider-speculative-loads-steps
void SpeculationRules::InnerConsiderLoads() {
mConsiderSpeculativeLoadsMicrotaskQueued = false;
// Step 1.
if (!mDocument || !mDocument->IsFullyActive()) {
return;
}
// Step 2.
UniquePtr<PrefetchCandidates> prefetchCandidates =
PrefetchCandidates::Create();
// Step 3.
for (auto& entry : mRuleSetsFromScript) {
entry.GetData()->ConsiderLoads(prefetchCandidates.get());
}
// Step 4.
// TODO(avandolder): Cancel and discard existing speculation rules prefetch
// records that are not still being speculated given prefetchCandidates.
// Step 5-6.
// Here, we group the candidates in-place, unlike the spec.
prefetchCandidates->Group();
// Step 7 runs in various cases when we decide to actually fire a prefetch
// based on the eagerness value of the candidates.
// Currently, we only support immediate eagerness, and we fire these
// prefetches now.
for ([[maybe_unused]] PrefetchCandidate& candidate :
prefetchCandidates->AsArray()) {
// TODO(avandolder): Create a prefetch record and start a referrer-initiated
// navigational prefetch given candidate.
}
}
} // namespace mozilla::dom