Add a debug v8 API SetDetachedWindowReason
A window is a Blink concept. This API marks the context as backing a detached window. This doesn't necessarily mean that the context is detached. Every time a JS function is called within a context that has a non-zero DetachedWindowReason, Runtime::kReportDetachedWindowAccess is invoked, which will report this call to Blink via a callback, which in turn can report number of such calls via UKM metrics. Bug: chromium:1018156 Change-Id: I67c89fef459f4efcb912229eed8a4f3ea3b60f54 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1862829 Auto-Submit: Bartek Nowierski <bartekn@chromium.org> Commit-Queue: Bartek Nowierski <bartekn@chromium.org> Reviewed-by: Georg Neis <neis@chromium.org> Reviewed-by: Jakob Gruber <jgruber@chromium.org> Reviewed-by: Ulan Degenbaev <ulan@chromium.org> Reviewed-by: Toon Verwaest <verwaest@chromium.org> Cr-Commit-Position: refs/heads/master@{#64707}
This commit is contained in:
parent
f09b1337e8
commit
63dc55568b
26
include/v8.h
26
include/v8.h
@ -9904,6 +9904,32 @@ class V8_EXPORT Context {
|
||||
*/
|
||||
void DetachGlobal();
|
||||
|
||||
/**
|
||||
* Reason for detaching a window.
|
||||
*/
|
||||
enum DetachedWindowReason {
|
||||
kWindowNotDetached = 0,
|
||||
kDetachedWindowByNavigation,
|
||||
kDetachedWindowByClosing,
|
||||
kDetachedWindowByOtherReason
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets a reason for detaching window, for reporting purposes.
|
||||
*
|
||||
* This API is experimental and may change or be deleted. Do not use!
|
||||
*
|
||||
* A window is a Blink concept. This API marks the context as backing
|
||||
* a detached window. This doesn't necessarily mean that the context is
|
||||
* detached.
|
||||
*
|
||||
* Every time a JS function is called within a context that has a non-zero
|
||||
* DetachedWindowReason, Runtime::kReportDetachedWindowAccess is invoked,
|
||||
* which will report this call to Blink via a callback, which in turn can
|
||||
* report number of such calls via UKM metrics.
|
||||
*/
|
||||
void SetDetachedWindowReason(DetachedWindowReason reason);
|
||||
|
||||
/**
|
||||
* Creates a new context and returns a handle to the newly allocated
|
||||
* context.
|
||||
|
@ -6002,6 +6002,20 @@ void Context::DetachGlobal() {
|
||||
isolate->bootstrapper()->DetachGlobal(context);
|
||||
}
|
||||
|
||||
void Context::SetDetachedWindowReason(DetachedWindowReason reason) {
|
||||
i::Handle<i::Context> context = Utils::OpenHandle(this);
|
||||
i::Isolate* isolate = context->GetIsolate();
|
||||
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate);
|
||||
CHECK(context->IsNativeContext());
|
||||
i::Handle<i::NativeContext> native_context =
|
||||
i::Handle<i::NativeContext>::cast(context);
|
||||
// Prioritize kDetachedWindowByNavigation over other reasons.
|
||||
if (native_context->GetDetachedWindowReason() !=
|
||||
kDetachedWindowByNavigation) {
|
||||
native_context->SetDetachedWindowReason(reason);
|
||||
}
|
||||
}
|
||||
|
||||
Local<v8::Object> Context::GetExtrasBindingObject() {
|
||||
i::Handle<i::Context> context = Utils::OpenHandle(this);
|
||||
i::Isolate* isolate = context->GetIsolate();
|
||||
|
@ -1953,6 +1953,14 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm,
|
||||
__ tst(r3, Operand(SharedFunctionInfo::IsNativeBit::kMask |
|
||||
SharedFunctionInfo::IsStrictBit::kMask));
|
||||
__ b(ne, &done_convert);
|
||||
|
||||
// Check if the window is marked as detached.
|
||||
Label detached_window, after_detached_window;
|
||||
__ LoadNativeContextSlot(Context::DETACHED_WINDOW_REASON_INDEX, r3);
|
||||
__ cmp(r3, Operand(Smi::zero()));
|
||||
__ b(ne, &detached_window);
|
||||
__ bind(&after_detached_window);
|
||||
|
||||
{
|
||||
// ----------- S t a t e -------------
|
||||
// -- r0 : the number of arguments (not including the receiver)
|
||||
@ -2024,6 +2032,15 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm,
|
||||
__ push(r1);
|
||||
__ CallRuntime(Runtime::kThrowConstructorNonCallableError);
|
||||
}
|
||||
|
||||
__ bind(&detached_window);
|
||||
{
|
||||
FrameScope frame(masm, StackFrame::INTERNAL);
|
||||
__ PushCallerSaved(kDontSaveFPRegs, r3);
|
||||
__ CallRuntime(Runtime::kReportDetachedWindowAccess);
|
||||
__ PopCallerSaved(kDontSaveFPRegs, r3);
|
||||
}
|
||||
__ jmp(&after_detached_window);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
@ -2361,6 +2361,14 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm,
|
||||
SharedFunctionInfo::IsNativeBit::kMask |
|
||||
SharedFunctionInfo::IsStrictBit::kMask,
|
||||
&done_convert);
|
||||
|
||||
// Check if the window is marked as detached.
|
||||
Label detached_window, after_detached_window;
|
||||
__ LoadNativeContextSlot(Context::DETACHED_WINDOW_REASON_INDEX, x3);
|
||||
__ CmpTagged(x3, Immediate(Smi::zero()));
|
||||
__ B(ne, &detached_window);
|
||||
__ bind(&after_detached_window);
|
||||
|
||||
{
|
||||
// ----------- S t a t e -------------
|
||||
// -- x0 : the number of arguments (not including the receiver)
|
||||
@ -2431,6 +2439,15 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm,
|
||||
__ PushArgument(x1);
|
||||
__ CallRuntime(Runtime::kThrowConstructorNonCallableError);
|
||||
}
|
||||
|
||||
__ bind(&detached_window);
|
||||
{
|
||||
FrameScope frame(masm, StackFrame::INTERNAL);
|
||||
__ PushCallerSaved(kDontSaveFPRegs, x3);
|
||||
__ CallRuntime(Runtime::kReportDetachedWindowAccess);
|
||||
__ PopCallerSaved(kDontSaveFPRegs, x3);
|
||||
}
|
||||
__ jmp(&after_detached_window);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
@ -2206,6 +2206,15 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm,
|
||||
Immediate(SharedFunctionInfo::IsNativeBit::kMask |
|
||||
SharedFunctionInfo::IsStrictBit::kMask));
|
||||
__ j(not_zero, &done_convert);
|
||||
|
||||
// Check if the window is marked as detached.
|
||||
Label detached_window, after_detached_window;
|
||||
__ LoadNativeContextSlot(Context::DETACHED_WINDOW_REASON_INDEX,
|
||||
kScratchRegister);
|
||||
__ Cmp(kScratchRegister, Smi::zero());
|
||||
__ j(not_zero, &detached_window);
|
||||
__ bind(&after_detached_window);
|
||||
|
||||
{
|
||||
// ----------- S t a t e -------------
|
||||
// -- rax : the number of arguments (not including the receiver)
|
||||
@ -2283,6 +2292,15 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm,
|
||||
__ Push(rdi);
|
||||
__ CallRuntime(Runtime::kThrowConstructorNonCallableError);
|
||||
}
|
||||
|
||||
__ bind(&detached_window);
|
||||
{
|
||||
FrameScope frame(masm, StackFrame::INTERNAL);
|
||||
__ PushCallerSaved(kDontSaveFPRegs, kScratchRegister);
|
||||
__ CallRuntime(Runtime::kReportDetachedWindowAccess);
|
||||
__ PopCallerSaved(kDontSaveFPRegs, kScratchRegister);
|
||||
}
|
||||
__ jmp(&after_detached_window);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
@ -1185,10 +1185,10 @@ void MacroAssembler::SmiCompare(Register dst, Smi src) {
|
||||
}
|
||||
|
||||
void MacroAssembler::Cmp(Register dst, Smi src) {
|
||||
DCHECK_NE(dst, kScratchRegister);
|
||||
if (src.value() == 0) {
|
||||
test_tagged(dst, dst);
|
||||
} else {
|
||||
DCHECK_NE(dst, kScratchRegister);
|
||||
Register constant_reg = GetSmiConstant(src);
|
||||
cmp_tagged(dst, constant_reg);
|
||||
}
|
||||
|
@ -1431,6 +1431,7 @@ Handle<NativeContext> Factory::NewNativeContext() {
|
||||
context->set_scope_info(ReadOnlyRoots(isolate()).native_scope_info());
|
||||
context->set_previous(Context::unchecked_cast(Smi::zero()));
|
||||
context->set_extension(*undefined_value());
|
||||
context->SetDetachedWindowReason(v8::Context::kWindowNotDetached);
|
||||
context->set_errors_thrown(Smi::zero());
|
||||
context->set_math_random_index(Smi::zero());
|
||||
context->set_serialized_objects(*empty_fixed_array());
|
||||
|
@ -485,5 +485,16 @@ STATIC_ASSERT(NativeContext::kSize ==
|
||||
(Context::SizeFor(NativeContext::NATIVE_CONTEXT_SLOTS) +
|
||||
kSystemPointerSize));
|
||||
|
||||
void NativeContext::SetDetachedWindowReason(
|
||||
v8::Context::DetachedWindowReason reason) {
|
||||
set_detached_window_reason(Smi::FromEnum(reason));
|
||||
}
|
||||
|
||||
v8::Context::DetachedWindowReason NativeContext::GetDetachedWindowReason()
|
||||
const {
|
||||
return static_cast<v8::Context::DetachedWindowReason>(
|
||||
detached_window_reason().value());
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -366,6 +366,7 @@ enum ContextLookupFlags {
|
||||
V(WEAKMAP_GET_INDEX, JSFunction, weakmap_get) \
|
||||
V(WEAKSET_ADD_INDEX, JSFunction, weakset_add) \
|
||||
V(OSR_CODE_CACHE_INDEX, WeakFixedArray, osr_code_cache) \
|
||||
V(DETACHED_WINDOW_REASON_INDEX, Smi, detached_window_reason) \
|
||||
NATIVE_CONTEXT_INTRINSIC_FUNCTIONS(V)
|
||||
|
||||
// A table of all script contexts. Every loaded top-level script with top-level
|
||||
@ -729,6 +730,9 @@ class NativeContext : public Context {
|
||||
void IncrementErrorsThrown();
|
||||
int GetErrorsThrown();
|
||||
|
||||
void SetDetachedWindowReason(v8::Context::DetachedWindowReason reason);
|
||||
v8::Context::DetachedWindowReason GetDetachedWindowReason() const;
|
||||
|
||||
private:
|
||||
STATIC_ASSERT(OffsetOfElementAt(EMBEDDER_DATA_INDEX) ==
|
||||
Internals::kNativeContextEmbedderDataOffset);
|
||||
|
@ -82,6 +82,19 @@ RUNTIME_FUNCTION(Runtime_ThrowSymbolAsyncIteratorInvalid) {
|
||||
isolate, NewTypeError(MessageTemplate::kSymbolAsyncIteratorInvalid));
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_ReportDetachedWindowAccess) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(0, args.length());
|
||||
Handle<NativeContext> native_context(isolate->context().native_context(),
|
||||
isolate);
|
||||
// TODO(bartekn,chromium:1018156): Report this to Blink, for it to emit it
|
||||
// via UKM. Use native_context->detached_window_reason().value()
|
||||
// This will be addressed as the first step after this CL lands.
|
||||
|
||||
// The return value isn't needed, but RUNTIME_FUNCTION sets it up.
|
||||
return ReadOnlyRoots(isolate).undefined_value();
|
||||
}
|
||||
|
||||
#define THROW_ERROR(isolate, args, call) \
|
||||
HandleScope scope(isolate); \
|
||||
DCHECK_LE(1, args.length()); \
|
||||
|
@ -221,6 +221,7 @@ namespace internal {
|
||||
F(NewTypeError, 2, 1) \
|
||||
F(OrdinaryHasInstance, 2, 1) \
|
||||
F(PromoteScheduledException, 0, 1) \
|
||||
F(ReportDetachedWindowAccess, 0, 1) \
|
||||
F(ReportMessage, 1, 1) \
|
||||
F(ReThrow, 1, 1) \
|
||||
F(RunMicrotaskCallback, 2, 1) \
|
||||
|
@ -9646,6 +9646,38 @@ TEST(DetachedAccesses) {
|
||||
}
|
||||
|
||||
|
||||
TEST(DetachedWindow) {
|
||||
LocalContext env1;
|
||||
v8::HandleScope scope(env1->GetIsolate());
|
||||
|
||||
// Create second environment.
|
||||
Local<ObjectTemplate> inner_global_template =
|
||||
FunctionTemplate::New(env1->GetIsolate())->InstanceTemplate();
|
||||
v8::Local<Context> env2 =
|
||||
Context::New(env1->GetIsolate(), nullptr, inner_global_template);
|
||||
|
||||
Local<Value> foo = v8_str("foo");
|
||||
|
||||
// Set same security token for env1 and env2.
|
||||
env1->SetSecurityToken(foo);
|
||||
env2->SetSecurityToken(foo);
|
||||
|
||||
{
|
||||
v8::Context::Scope scope(env2);
|
||||
CompileRun("function fun() { }");
|
||||
CHECK(env1->Global()
|
||||
->Set(env1.local(), v8_str("fun"), CompileRun("fun"))
|
||||
.FromJust());
|
||||
}
|
||||
|
||||
env2->SetDetachedWindowReason(v8::Context::kDetachedWindowByNavigation);
|
||||
|
||||
CompileRun("fun()");
|
||||
// This merely tests for not crashing, because currently
|
||||
// Runtime_ReportDetachedWindowAccess does nothing.
|
||||
}
|
||||
|
||||
|
||||
static bool allowed_access = false;
|
||||
static bool AccessBlocker(Local<v8::Context> accessing_context,
|
||||
Local<v8::Object> accessed_object,
|
||||
|
Loading…
Reference in New Issue
Block a user