2020-05-11 14:20:39 +00:00
|
|
|
/* 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 "PreloaderBase.h"
|
|
|
|
|
|
|
|
#include "mozilla/dom/Document.h"
|
2020-08-24 11:24:21 +00:00
|
|
|
#include "mozilla/Telemetry.h"
|
2020-06-23 17:18:20 +00:00
|
|
|
#include "nsContentUtils.h"
|
2020-05-11 14:20:39 +00:00
|
|
|
#include "nsIAsyncVerifyRedirectCallback.h"
|
Backed out 11 changesets (bug 1817309, bug 1817315, bug 1817313, bug 1817317, bug 1817310, bug 1817314, bug 1817308, bug 1817312, bug 1817311, bug 1817316, bug 1817318) for causing build bustages on nss_secutil.h CLOSED TREE
Backed out changeset 288b4848b023 (bug 1817308)
Backed out changeset 6ac5107e1c81 (bug 1817309)
Backed out changeset 6f6e7a258930 (bug 1817310)
Backed out changeset 32cadd62fec3 (bug 1817311)
Backed out changeset 5bfcee2bfb8b (bug 1817312)
Backed out changeset bfd59ae03c4f (bug 1817313)
Backed out changeset 0080bdd6ab3f (bug 1817314)
Backed out changeset e1e81a3a933d (bug 1817315)
Backed out changeset 151d9b5d1883 (bug 1817316)
Backed out changeset 1b03cabc6e5a (bug 1817317)
Backed out changeset 52b44c5d4b08 (bug 1817318)
2023-02-19 16:37:26 +00:00
|
|
|
#include "nsIChannel.h"
|
2020-05-11 14:20:39 +00:00
|
|
|
#include "nsILoadGroup.h"
|
|
|
|
#include "nsIInterfaceRequestorUtils.h"
|
2020-11-23 16:21:38 +00:00
|
|
|
#include "nsIRedirectResultListener.h"
|
2020-05-11 14:20:39 +00:00
|
|
|
|
2020-05-12 12:15:57 +00:00
|
|
|
// Change this if we want to cancel and remove the associated preload on removal
|
|
|
|
// of all <link rel=preload> tags from the tree.
|
|
|
|
constexpr static bool kCancelAndRemovePreloadOnZeroReferences = false;
|
|
|
|
|
2020-05-11 14:20:39 +00:00
|
|
|
namespace mozilla {
|
|
|
|
|
2020-07-15 12:47:41 +00:00
|
|
|
PreloaderBase::UsageTimer::UsageTimer(PreloaderBase* aPreload,
|
|
|
|
dom::Document* aDocument)
|
|
|
|
: mDocument(aDocument), mPreload(aPreload) {}
|
|
|
|
|
2020-09-24 16:01:50 +00:00
|
|
|
class PreloaderBase::RedirectSink final : public nsIInterfaceRequestor,
|
|
|
|
public nsIChannelEventSink,
|
|
|
|
public nsIRedirectResultListener {
|
|
|
|
RedirectSink() = delete;
|
|
|
|
virtual ~RedirectSink();
|
|
|
|
|
|
|
|
public:
|
|
|
|
NS_DECL_THREADSAFE_ISUPPORTS
|
|
|
|
NS_DECL_NSIINTERFACEREQUESTOR
|
|
|
|
NS_DECL_NSICHANNELEVENTSINK
|
|
|
|
NS_DECL_NSIREDIRECTRESULTLISTENER
|
|
|
|
|
|
|
|
RedirectSink(PreloaderBase* aPreloader, nsIInterfaceRequestor* aCallbacks);
|
|
|
|
|
|
|
|
private:
|
2020-09-24 16:38:35 +00:00
|
|
|
MainThreadWeakPtr<PreloaderBase> mPreloader;
|
2020-09-24 16:01:50 +00:00
|
|
|
nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
|
|
|
|
nsCOMPtr<nsIChannel> mRedirectChannel;
|
|
|
|
};
|
|
|
|
|
2020-05-11 14:20:39 +00:00
|
|
|
PreloaderBase::RedirectSink::RedirectSink(PreloaderBase* aPreloader,
|
|
|
|
nsIInterfaceRequestor* aCallbacks)
|
2020-09-24 16:01:50 +00:00
|
|
|
: mPreloader(aPreloader), mCallbacks(aCallbacks) {}
|
|
|
|
|
2020-09-24 16:38:35 +00:00
|
|
|
PreloaderBase::RedirectSink::~RedirectSink() = default;
|
2020-05-11 14:20:39 +00:00
|
|
|
|
|
|
|
NS_IMPL_ISUPPORTS(PreloaderBase::RedirectSink, nsIInterfaceRequestor,
|
|
|
|
nsIChannelEventSink, nsIRedirectResultListener)
|
|
|
|
|
|
|
|
NS_IMETHODIMP PreloaderBase::RedirectSink::AsyncOnChannelRedirect(
|
|
|
|
nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags,
|
|
|
|
nsIAsyncVerifyRedirectCallback* aCallback) {
|
2020-09-24 16:01:50 +00:00
|
|
|
MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
|
|
|
|
|
2020-05-11 14:20:39 +00:00
|
|
|
mRedirectChannel = aNewChannel;
|
|
|
|
|
2020-05-18 12:18:14 +00:00
|
|
|
// Deliberately adding this before confirmation.
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
|
|
aNewChannel->GetOriginalURI(getter_AddRefs(uri));
|
2020-09-24 16:01:50 +00:00
|
|
|
if (mPreloader) {
|
|
|
|
mPreloader->mRedirectRecords.AppendElement(
|
|
|
|
RedirectRecord(aFlags, uri.forget()));
|
|
|
|
}
|
2020-05-18 12:18:14 +00:00
|
|
|
|
2020-05-11 14:20:39 +00:00
|
|
|
if (mCallbacks) {
|
|
|
|
nsCOMPtr<nsIChannelEventSink> sink(do_GetInterface(mCallbacks));
|
|
|
|
if (sink) {
|
|
|
|
return sink->AsyncOnChannelRedirect(aOldChannel, aNewChannel, aFlags,
|
|
|
|
aCallback);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
aCallback->OnRedirectVerifyCallback(NS_OK);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2022-11-16 08:49:45 +00:00
|
|
|
NS_IMETHODIMP PreloaderBase::RedirectSink::OnRedirectResult(nsresult status) {
|
|
|
|
if (NS_SUCCEEDED(status) && mRedirectChannel) {
|
2020-05-11 14:20:39 +00:00
|
|
|
mPreloader->mChannel = std::move(mRedirectChannel);
|
|
|
|
} else {
|
|
|
|
mRedirectChannel = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mCallbacks) {
|
|
|
|
nsCOMPtr<nsIRedirectResultListener> sink(do_GetInterface(mCallbacks));
|
|
|
|
if (sink) {
|
2022-11-16 08:49:45 +00:00
|
|
|
return sink->OnRedirectResult(status);
|
2020-05-11 14:20:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP PreloaderBase::RedirectSink::GetInterface(const nsIID& aIID,
|
|
|
|
void** aResult) {
|
|
|
|
NS_ENSURE_ARG_POINTER(aResult);
|
|
|
|
|
|
|
|
if (aIID.Equals(NS_GET_IID(nsIChannelEventSink)) ||
|
|
|
|
aIID.Equals(NS_GET_IID(nsIRedirectResultListener))) {
|
|
|
|
return QueryInterface(aIID, aResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mCallbacks) {
|
|
|
|
return mCallbacks->GetInterface(aIID, aResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
*aResult = nullptr;
|
|
|
|
return NS_ERROR_NO_INTERFACE;
|
|
|
|
}
|
|
|
|
|
|
|
|
PreloaderBase::~PreloaderBase() { MOZ_ASSERT(NS_IsMainThread()); }
|
|
|
|
|
|
|
|
// static
|
|
|
|
void PreloaderBase::AddLoadBackgroundFlag(nsIChannel* aChannel) {
|
|
|
|
nsLoadFlags loadFlags;
|
|
|
|
aChannel->GetLoadFlags(&loadFlags);
|
|
|
|
aChannel->SetLoadFlags(loadFlags | nsIRequest::LOAD_BACKGROUND);
|
|
|
|
}
|
|
|
|
|
2020-06-18 14:06:34 +00:00
|
|
|
void PreloaderBase::NotifyOpen(const PreloadHashKey& aKey,
|
|
|
|
dom::Document* aDocument, bool aIsPreload) {
|
2021-02-01 14:59:48 +00:00
|
|
|
if (aDocument) {
|
|
|
|
DebugOnly<bool> alreadyRegistered =
|
|
|
|
aDocument->Preloads().RegisterPreload(aKey, this);
|
2020-05-11 14:04:36 +00:00
|
|
|
// This means there is already a preload registered under this key in this
|
|
|
|
// document. We only allow replacement when this is a regular load.
|
|
|
|
// Otherwise, this should never happen and is a suspected misuse of the API.
|
2021-02-01 14:59:48 +00:00
|
|
|
MOZ_ASSERT_IF(alreadyRegistered, !aIsPreload);
|
2020-05-11 14:04:36 +00:00
|
|
|
}
|
2020-05-11 14:20:39 +00:00
|
|
|
|
2020-06-18 14:06:34 +00:00
|
|
|
mKey = aKey;
|
2020-05-11 14:20:39 +00:00
|
|
|
mIsUsed = !aIsPreload;
|
2020-08-20 22:01:29 +00:00
|
|
|
|
|
|
|
if (!mIsUsed && !mUsageTimer) {
|
|
|
|
auto callback = MakeRefPtr<UsageTimer>(this, aDocument);
|
|
|
|
NS_NewTimerWithCallback(getter_AddRefs(mUsageTimer), callback, 10000,
|
|
|
|
nsITimer::TYPE_ONE_SHOT);
|
|
|
|
}
|
2020-08-24 11:24:21 +00:00
|
|
|
|
|
|
|
ReportUsageTelemetry();
|
2020-05-11 14:43:05 +00:00
|
|
|
}
|
|
|
|
|
2020-06-18 14:06:34 +00:00
|
|
|
void PreloaderBase::NotifyOpen(const PreloadHashKey& aKey, nsIChannel* aChannel,
|
2020-05-11 14:43:05 +00:00
|
|
|
dom::Document* aDocument, bool aIsPreload) {
|
|
|
|
NotifyOpen(aKey, aDocument, aIsPreload);
|
|
|
|
mChannel = aChannel;
|
2020-05-11 14:20:39 +00:00
|
|
|
|
|
|
|
nsCOMPtr<nsIInterfaceRequestor> callbacks;
|
|
|
|
mChannel->GetNotificationCallbacks(getter_AddRefs(callbacks));
|
|
|
|
RefPtr<RedirectSink> sink(new RedirectSink(this, callbacks));
|
|
|
|
mChannel->SetNotificationCallbacks(sink);
|
|
|
|
}
|
|
|
|
|
2020-05-18 12:18:14 +00:00
|
|
|
void PreloaderBase::NotifyUsage(LoadBackground aLoadBackground) {
|
|
|
|
if (!mIsUsed && mChannel && aLoadBackground == LoadBackground::Drop) {
|
2020-05-11 14:20:39 +00:00
|
|
|
nsLoadFlags loadFlags;
|
|
|
|
mChannel->GetLoadFlags(&loadFlags);
|
|
|
|
|
|
|
|
// Preloads are initially set the LOAD_BACKGROUND flag. When becoming
|
|
|
|
// regular loads by hitting its consuming tag, we need to drop that flag,
|
|
|
|
// which also means to re-add the request from/to it's loadgroup to reflect
|
|
|
|
// that flag change.
|
|
|
|
if (loadFlags & nsIRequest::LOAD_BACKGROUND) {
|
|
|
|
nsCOMPtr<nsILoadGroup> loadGroup;
|
|
|
|
mChannel->GetLoadGroup(getter_AddRefs(loadGroup));
|
|
|
|
|
|
|
|
if (loadGroup) {
|
|
|
|
nsresult status;
|
|
|
|
mChannel->GetStatus(&status);
|
|
|
|
|
|
|
|
nsresult rv = loadGroup->RemoveRequest(mChannel, nullptr, status);
|
|
|
|
mChannel->SetLoadFlags(loadFlags & ~nsIRequest::LOAD_BACKGROUND);
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
|
|
loadGroup->AddRequest(mChannel, nullptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mIsUsed = true;
|
2020-08-24 11:24:21 +00:00
|
|
|
ReportUsageTelemetry();
|
2020-06-23 17:18:20 +00:00
|
|
|
CancelUsageTimer();
|
2020-05-11 14:20:39 +00:00
|
|
|
}
|
|
|
|
|
2020-05-18 12:18:14 +00:00
|
|
|
void PreloaderBase::RemoveSelf(dom::Document* aDocument) {
|
2020-05-14 11:24:03 +00:00
|
|
|
if (aDocument) {
|
2020-06-18 14:06:34 +00:00
|
|
|
aDocument->Preloads().DeregisterPreload(mKey);
|
2020-05-14 11:24:03 +00:00
|
|
|
}
|
2020-05-18 12:18:14 +00:00
|
|
|
}
|
2020-05-14 11:24:03 +00:00
|
|
|
|
2020-05-18 12:18:14 +00:00
|
|
|
void PreloaderBase::NotifyRestart(dom::Document* aDocument,
|
|
|
|
PreloaderBase* aNewPreloader) {
|
|
|
|
RemoveSelf(aDocument);
|
2020-05-11 14:20:39 +00:00
|
|
|
mKey = PreloadHashKey();
|
|
|
|
|
2020-06-23 17:18:20 +00:00
|
|
|
CancelUsageTimer();
|
|
|
|
|
2020-05-11 14:20:39 +00:00
|
|
|
if (aNewPreloader) {
|
|
|
|
aNewPreloader->mNodes = std::move(mNodes);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PreloaderBase::NotifyStart(nsIRequest* aRequest) {
|
2020-05-25 18:38:36 +00:00
|
|
|
// If there is no channel assigned on this preloader, we are not between
|
|
|
|
// channel switching, so we can freely update the mShouldFireLoadEvent using
|
|
|
|
// the given channel.
|
|
|
|
if (mChannel && !SameCOMIdentity(aRequest, mChannel)) {
|
2020-05-11 14:20:39 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-05-25 18:38:36 +00:00
|
|
|
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
|
2020-05-11 14:20:39 +00:00
|
|
|
if (!httpChannel) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if the load is cross origin without CORS, or the CORS access is rejected,
|
|
|
|
// always fire load event to avoid leaking site information.
|
|
|
|
nsresult rv;
|
|
|
|
nsCOMPtr<nsILoadInfo> loadInfo = httpChannel->LoadInfo();
|
|
|
|
mShouldFireLoadEvent =
|
|
|
|
loadInfo->GetTainting() == LoadTainting::Opaque ||
|
|
|
|
(loadInfo->GetTainting() == LoadTainting::CORS &&
|
|
|
|
(NS_FAILED(httpChannel->GetStatus(&rv)) || NS_FAILED(rv)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void PreloaderBase::NotifyStop(nsIRequest* aRequest, nsresult aStatus) {
|
|
|
|
// Filter out notifications that may be arriving from the old channel before
|
|
|
|
// restarting this request.
|
|
|
|
if (!SameCOMIdentity(aRequest, mChannel)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
NotifyStop(aStatus);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PreloaderBase::NotifyStop(nsresult aStatus) {
|
|
|
|
mOnStopStatus.emplace(aStatus);
|
|
|
|
|
2020-08-04 11:27:07 +00:00
|
|
|
nsTArray<nsWeakPtr> nodes = std::move(mNodes);
|
2020-05-11 14:20:39 +00:00
|
|
|
|
|
|
|
for (nsWeakPtr& weak : nodes) {
|
|
|
|
nsCOMPtr<nsINode> node = do_QueryReferent(weak);
|
|
|
|
if (node) {
|
|
|
|
NotifyNodeEvent(node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mChannel = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PreloaderBase::AddLinkPreloadNode(nsINode* aNode) {
|
|
|
|
if (mOnStopStatus) {
|
|
|
|
return NotifyNodeEvent(aNode);
|
|
|
|
}
|
|
|
|
|
|
|
|
mNodes.AppendElement(do_GetWeakReference(aNode));
|
|
|
|
}
|
|
|
|
|
|
|
|
void PreloaderBase::RemoveLinkPreloadNode(nsINode* aNode) {
|
|
|
|
// Note that do_GetWeakReference returns the internal weak proxy, which is
|
|
|
|
// always the same, so we can use it to search the array using default
|
|
|
|
// comparator.
|
|
|
|
nsWeakPtr node = do_GetWeakReference(aNode);
|
|
|
|
mNodes.RemoveElement(node);
|
|
|
|
|
2020-05-12 12:15:57 +00:00
|
|
|
if (kCancelAndRemovePreloadOnZeroReferences && mNodes.Length() == 0 &&
|
|
|
|
!mIsUsed) {
|
2020-05-11 14:04:36 +00:00
|
|
|
// Keep a reference, because the following call may release us. The caller
|
|
|
|
// may use a WeakPtr to access this.
|
|
|
|
RefPtr<PreloaderBase> self(this);
|
2020-05-18 12:18:14 +00:00
|
|
|
RemoveSelf(aNode->OwnerDoc());
|
2020-05-11 14:20:39 +00:00
|
|
|
|
|
|
|
if (mChannel) {
|
2022-09-08 09:11:19 +00:00
|
|
|
mChannel->CancelWithReason(NS_BINDING_ABORTED,
|
|
|
|
"PreloaderBase::RemoveLinkPreloadNode"_ns);
|
2020-05-11 14:20:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PreloaderBase::NotifyNodeEvent(nsINode* aNode) {
|
2020-05-11 14:05:22 +00:00
|
|
|
PreloadService::NotifyNodeEvent(
|
|
|
|
aNode, mShouldFireLoadEvent || NS_SUCCEEDED(*mOnStopStatus));
|
2020-05-11 14:20:39 +00:00
|
|
|
}
|
|
|
|
|
2020-06-23 17:18:20 +00:00
|
|
|
void PreloaderBase::CancelUsageTimer() {
|
|
|
|
if (mUsageTimer) {
|
|
|
|
mUsageTimer->Cancel();
|
|
|
|
mUsageTimer = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-24 11:24:21 +00:00
|
|
|
void PreloaderBase::ReportUsageTelemetry() {
|
|
|
|
if (mUsageTelementryReported) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
mUsageTelementryReported = true;
|
|
|
|
|
|
|
|
if (mKey.As() == PreloadHashKey::ResourceType::NONE) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The labels are structured as type1-used, type1-unused, type2-used, ...
|
|
|
|
// The first "as" resource type is NONE with value 0.
|
|
|
|
auto index = (static_cast<uint32_t>(mKey.As()) - 1) * 2;
|
|
|
|
if (!mIsUsed) {
|
|
|
|
++index;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto label = static_cast<Telemetry::LABELS_REL_PRELOAD_MISS_RATIO>(index);
|
|
|
|
Telemetry::AccumulateCategorical(label);
|
|
|
|
}
|
|
|
|
|
2020-05-11 14:20:39 +00:00
|
|
|
nsresult PreloaderBase::AsyncConsume(nsIStreamListener* aListener) {
|
|
|
|
// We want to return an error so that consumers can't ever use a preload to
|
|
|
|
// consume data unless it's properly implemented.
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
2020-05-18 12:18:14 +00:00
|
|
|
// PreloaderBase::RedirectRecord
|
|
|
|
|
|
|
|
nsCString PreloaderBase::RedirectRecord::Spec() const {
|
|
|
|
nsCOMPtr<nsIURI> noFragment;
|
|
|
|
NS_GetURIWithoutRef(mURI, getter_AddRefs(noFragment));
|
|
|
|
MOZ_ASSERT(noFragment);
|
|
|
|
return noFragment->GetSpecOrDefault();
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCString PreloaderBase::RedirectRecord::Fragment() const {
|
|
|
|
nsCString fragment;
|
|
|
|
mURI->GetRef(fragment);
|
|
|
|
return fragment;
|
|
|
|
}
|
|
|
|
|
2020-06-23 17:18:20 +00:00
|
|
|
// PreloaderBase::UsageTimer
|
|
|
|
|
2021-09-07 08:01:18 +00:00
|
|
|
NS_IMPL_ISUPPORTS(PreloaderBase::UsageTimer, nsITimerCallback, nsINamed)
|
2020-06-23 17:18:20 +00:00
|
|
|
|
|
|
|
NS_IMETHODIMP PreloaderBase::UsageTimer::Notify(nsITimer* aTimer) {
|
|
|
|
if (!mPreload || !mDocument) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_ASSERT(aTimer == mPreload->mUsageTimer);
|
|
|
|
mPreload->mUsageTimer = nullptr;
|
|
|
|
|
|
|
|
if (mPreload->IsUsed()) {
|
|
|
|
// Left in the hashtable, but marked as used. This is a valid case, and we
|
|
|
|
// don't want to emit a warning for this preload then.
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2020-08-24 11:24:21 +00:00
|
|
|
mPreload->ReportUsageTelemetry();
|
|
|
|
|
2020-06-23 17:18:20 +00:00
|
|
|
// PreloadHashKey overrides GetKey, we need to use the nsURIHashKey one to get
|
|
|
|
// the URI.
|
|
|
|
nsIURI* uri = static_cast<nsURIHashKey*>(&mPreload->mKey)->GetKey();
|
|
|
|
if (!uri) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2020-08-20 22:04:36 +00:00
|
|
|
nsString spec;
|
|
|
|
NS_GetSanitizedURIStringFromURI(uri, spec);
|
2020-06-23 17:18:20 +00:00
|
|
|
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "DOM"_ns,
|
|
|
|
mDocument, nsContentUtils::eDOM_PROPERTIES,
|
|
|
|
"UnusedLinkPreloadPending",
|
2020-08-20 22:04:36 +00:00
|
|
|
nsTArray<nsString>({std::move(spec)}));
|
2020-06-23 17:18:20 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2021-09-07 08:01:18 +00:00
|
|
|
NS_IMETHODIMP
|
|
|
|
PreloaderBase::UsageTimer::GetName(nsACString& aName) {
|
|
|
|
aName.AssignLiteral("PreloaderBase::UsageTimer");
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2020-05-11 14:20:39 +00:00
|
|
|
} // namespace mozilla
|