/* -*- 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 "gtest/gtest.h" #include "js/TypeDecls.h" #include "js/Value.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/Promise-inl.h" #include "xpcpublic.h" using namespace mozilla; using namespace mozilla::dom; struct TraceCounts { int32_t mValue = 0; int32_t mId = 0; int32_t mObject = 0; int32_t mWrapperCache = 0; int32_t mTenuredHeapObject = 0; int32_t mString = 0; int32_t mScript = 0; int32_t mFunction = 0; }; struct DummyCallbacks final : public TraceCallbacks { void Trace(JS::Heap*, const char*, void* aClosure) const override { static_cast(aClosure)->mValue++; } void Trace(JS::Heap*, const char*, void* aClosure) const override { static_cast(aClosure)->mId++; } void Trace(JS::Heap*, const char*, void* aClosure) const override { static_cast(aClosure)->mObject++; } void Trace(nsWrapperCache*, const char* aName, void* aClosure) const override { static_cast(aClosure)->mWrapperCache++; } void Trace(JS::TenuredHeap*, const char*, void* aClosure) const override { static_cast(aClosure)->mTenuredHeapObject++; } void Trace(JS::Heap*, const char*, void* aClosure) const override { static_cast(aClosure)->mString++; } void Trace(JS::Heap*, const char*, void* aClosure) const override { static_cast(aClosure)->mScript++; } void Trace(JS::Heap*, const char*, void* aClosure) const override { static_cast(aClosure)->mFunction++; } }; TEST(NativeThenHandler, TraceValue) { auto onResolve = [](JSContext*, JS::Handle, ErrorResult&, JS::Handle) -> already_AddRefed { return nullptr; }; auto onReject = [](JSContext*, JS::Handle, ErrorResult&, JS::Handle) -> already_AddRefed { return nullptr; }; // Explicit type for backward compatibility with clang<7 / gcc<8 using HandlerType = NativeThenHandler, std::tuple>; RefPtr handler = new HandlerType( nullptr, Some(onResolve), Some(onReject), std::make_tuple(), std::make_tuple(JS::UndefinedHandleValue)); TraceCounts counts; NS_CYCLE_COLLECTION_PARTICIPANT(HandlerType) ->Trace(handler.get(), DummyCallbacks(), &counts); EXPECT_EQ(counts.mValue, 1); } TEST(NativeThenHandler, TraceObject) { auto onResolve = [](JSContext*, JS::Handle, ErrorResult&, JS::Handle) -> already_AddRefed { return nullptr; }; auto onReject = [](JSContext*, JS::Handle, ErrorResult&, JS::Handle) -> already_AddRefed { return nullptr; }; AutoJSAPI jsapi; MOZ_ALWAYS_TRUE(jsapi.Init(xpc::PrivilegedJunkScope())); JSContext* cx = jsapi.cx(); JS::Rooted obj(cx, JS_NewPlainObject(cx)); // Explicit type for backward compatibility with clang<7 / gcc<8 using HandlerType = NativeThenHandler, std::tuple>; RefPtr handler = new HandlerType( nullptr, Some(onResolve), Some(onReject), std::make_tuple(), std::make_tuple(JS::HandleObject(obj))); TraceCounts counts; NS_CYCLE_COLLECTION_PARTICIPANT(HandlerType) ->Trace(handler.get(), DummyCallbacks(), &counts); EXPECT_EQ(counts.mObject, 1); } TEST(NativeThenHandler, TraceMixed) { auto onResolve = [](JSContext*, JS::Handle, ErrorResult&, nsIGlobalObject*, Promise*, JS::Handle, JS::Handle) -> already_AddRefed { return nullptr; }; auto onReject = [](JSContext*, JS::Handle, ErrorResult&, nsIGlobalObject*, Promise*, JS::Handle, JS::Handle) -> already_AddRefed { return nullptr; }; AutoJSAPI jsapi; MOZ_ALWAYS_TRUE(jsapi.Init(xpc::PrivilegedJunkScope())); JSContext* cx = jsapi.cx(); nsCOMPtr global = xpc::CurrentNativeGlobal(cx); JS::Rooted obj(cx, JS_NewPlainObject(cx)); RefPtr promise = Promise::Create(global, IgnoreErrors()); // Explicit type for backward compatibility with clang<7 / gcc<8 using HandlerType = NativeThenHandler, RefPtr>, std::tuple>; RefPtr handler = new HandlerType( nullptr, Some(onResolve), Some(onReject), std::make_tuple(global, promise), std::make_tuple(JS::UndefinedHandleValue, JS::HandleObject(obj))); TraceCounts counts; NS_CYCLE_COLLECTION_PARTICIPANT(HandlerType) ->Trace(handler.get(), DummyCallbacks(), &counts); EXPECT_EQ(counts.mValue, 1); EXPECT_EQ(counts.mObject, 1); }