Commit 63dc5556 authored by Bartek Nowierski's avatar Bartek Nowierski Committed by Commit Bot

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: 's avatarGeorg Neis <neis@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Reviewed-by: 's avatarToon Verwaest <verwaest@chromium.org>
Cr-Commit-Position: refs/heads/master@{#64707}
parent f09b1337
...@@ -9904,6 +9904,32 @@ class V8_EXPORT Context { ...@@ -9904,6 +9904,32 @@ class V8_EXPORT Context {
*/ */
void DetachGlobal(); 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 * Creates a new context and returns a handle to the newly allocated
* context. * context.
......
...@@ -6002,6 +6002,20 @@ void Context::DetachGlobal() { ...@@ -6002,6 +6002,20 @@ void Context::DetachGlobal() {
isolate->bootstrapper()->DetachGlobal(context); 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() { Local<v8::Object> Context::GetExtrasBindingObject() {
i::Handle<i::Context> context = Utils::OpenHandle(this); i::Handle<i::Context> context = Utils::OpenHandle(this);
i::Isolate* isolate = context->GetIsolate(); i::Isolate* isolate = context->GetIsolate();
......
...@@ -1953,6 +1953,14 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm, ...@@ -1953,6 +1953,14 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm,
__ tst(r3, Operand(SharedFunctionInfo::IsNativeBit::kMask | __ tst(r3, Operand(SharedFunctionInfo::IsNativeBit::kMask |
SharedFunctionInfo::IsStrictBit::kMask)); SharedFunctionInfo::IsStrictBit::kMask));
__ b(ne, &done_convert); __ 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 ------------- // ----------- S t a t e -------------
// -- r0 : the number of arguments (not including the receiver) // -- r0 : the number of arguments (not including the receiver)
...@@ -2024,6 +2032,15 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm, ...@@ -2024,6 +2032,15 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm,
__ push(r1); __ push(r1);
__ CallRuntime(Runtime::kThrowConstructorNonCallableError); __ 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 { namespace {
......
...@@ -2361,6 +2361,14 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm, ...@@ -2361,6 +2361,14 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm,
SharedFunctionInfo::IsNativeBit::kMask | SharedFunctionInfo::IsNativeBit::kMask |
SharedFunctionInfo::IsStrictBit::kMask, SharedFunctionInfo::IsStrictBit::kMask,
&done_convert); &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 ------------- // ----------- S t a t e -------------
// -- x0 : the number of arguments (not including the receiver) // -- x0 : the number of arguments (not including the receiver)
...@@ -2431,6 +2439,15 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm, ...@@ -2431,6 +2439,15 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm,
__ PushArgument(x1); __ PushArgument(x1);
__ CallRuntime(Runtime::kThrowConstructorNonCallableError); __ 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 { namespace {
......
...@@ -2206,6 +2206,15 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm, ...@@ -2206,6 +2206,15 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm,
Immediate(SharedFunctionInfo::IsNativeBit::kMask | Immediate(SharedFunctionInfo::IsNativeBit::kMask |
SharedFunctionInfo::IsStrictBit::kMask)); SharedFunctionInfo::IsStrictBit::kMask));
__ j(not_zero, &done_convert); __ 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 ------------- // ----------- S t a t e -------------
// -- rax : the number of arguments (not including the receiver) // -- rax : the number of arguments (not including the receiver)
...@@ -2283,6 +2292,15 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm, ...@@ -2283,6 +2292,15 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm,
__ Push(rdi); __ Push(rdi);
__ CallRuntime(Runtime::kThrowConstructorNonCallableError); __ 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 { namespace {
......
...@@ -1185,10 +1185,10 @@ void MacroAssembler::SmiCompare(Register dst, Smi src) { ...@@ -1185,10 +1185,10 @@ void MacroAssembler::SmiCompare(Register dst, Smi src) {
} }
void MacroAssembler::Cmp(Register dst, Smi src) { void MacroAssembler::Cmp(Register dst, Smi src) {
DCHECK_NE(dst, kScratchRegister);
if (src.value() == 0) { if (src.value() == 0) {
test_tagged(dst, dst); test_tagged(dst, dst);
} else { } else {
DCHECK_NE(dst, kScratchRegister);
Register constant_reg = GetSmiConstant(src); Register constant_reg = GetSmiConstant(src);
cmp_tagged(dst, constant_reg); cmp_tagged(dst, constant_reg);
} }
......
...@@ -1431,6 +1431,7 @@ Handle<NativeContext> Factory::NewNativeContext() { ...@@ -1431,6 +1431,7 @@ Handle<NativeContext> Factory::NewNativeContext() {
context->set_scope_info(ReadOnlyRoots(isolate()).native_scope_info()); context->set_scope_info(ReadOnlyRoots(isolate()).native_scope_info());
context->set_previous(Context::unchecked_cast(Smi::zero())); context->set_previous(Context::unchecked_cast(Smi::zero()));
context->set_extension(*undefined_value()); context->set_extension(*undefined_value());
context->SetDetachedWindowReason(v8::Context::kWindowNotDetached);
context->set_errors_thrown(Smi::zero()); context->set_errors_thrown(Smi::zero());
context->set_math_random_index(Smi::zero()); context->set_math_random_index(Smi::zero());
context->set_serialized_objects(*empty_fixed_array()); context->set_serialized_objects(*empty_fixed_array());
......
...@@ -485,5 +485,16 @@ STATIC_ASSERT(NativeContext::kSize == ...@@ -485,5 +485,16 @@ STATIC_ASSERT(NativeContext::kSize ==
(Context::SizeFor(NativeContext::NATIVE_CONTEXT_SLOTS) + (Context::SizeFor(NativeContext::NATIVE_CONTEXT_SLOTS) +
kSystemPointerSize)); 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 internal
} // namespace v8 } // namespace v8
...@@ -366,6 +366,7 @@ enum ContextLookupFlags { ...@@ -366,6 +366,7 @@ enum ContextLookupFlags {
V(WEAKMAP_GET_INDEX, JSFunction, weakmap_get) \ V(WEAKMAP_GET_INDEX, JSFunction, weakmap_get) \
V(WEAKSET_ADD_INDEX, JSFunction, weakset_add) \ V(WEAKSET_ADD_INDEX, JSFunction, weakset_add) \
V(OSR_CODE_CACHE_INDEX, WeakFixedArray, osr_code_cache) \ V(OSR_CODE_CACHE_INDEX, WeakFixedArray, osr_code_cache) \
V(DETACHED_WINDOW_REASON_INDEX, Smi, detached_window_reason) \
NATIVE_CONTEXT_INTRINSIC_FUNCTIONS(V) NATIVE_CONTEXT_INTRINSIC_FUNCTIONS(V)
// A table of all script contexts. Every loaded top-level script with top-level // A table of all script contexts. Every loaded top-level script with top-level
...@@ -729,6 +730,9 @@ class NativeContext : public Context { ...@@ -729,6 +730,9 @@ class NativeContext : public Context {
void IncrementErrorsThrown(); void IncrementErrorsThrown();
int GetErrorsThrown(); int GetErrorsThrown();
void SetDetachedWindowReason(v8::Context::DetachedWindowReason reason);
v8::Context::DetachedWindowReason GetDetachedWindowReason() const;
private: private:
STATIC_ASSERT(OffsetOfElementAt(EMBEDDER_DATA_INDEX) == STATIC_ASSERT(OffsetOfElementAt(EMBEDDER_DATA_INDEX) ==
Internals::kNativeContextEmbedderDataOffset); Internals::kNativeContextEmbedderDataOffset);
......
...@@ -82,6 +82,19 @@ RUNTIME_FUNCTION(Runtime_ThrowSymbolAsyncIteratorInvalid) { ...@@ -82,6 +82,19 @@ RUNTIME_FUNCTION(Runtime_ThrowSymbolAsyncIteratorInvalid) {
isolate, NewTypeError(MessageTemplate::kSymbolAsyncIteratorInvalid)); 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) \ #define THROW_ERROR(isolate, args, call) \
HandleScope scope(isolate); \ HandleScope scope(isolate); \
DCHECK_LE(1, args.length()); \ DCHECK_LE(1, args.length()); \
......
...@@ -221,6 +221,7 @@ namespace internal { ...@@ -221,6 +221,7 @@ namespace internal {
F(NewTypeError, 2, 1) \ F(NewTypeError, 2, 1) \
F(OrdinaryHasInstance, 2, 1) \ F(OrdinaryHasInstance, 2, 1) \
F(PromoteScheduledException, 0, 1) \ F(PromoteScheduledException, 0, 1) \
F(ReportDetachedWindowAccess, 0, 1) \
F(ReportMessage, 1, 1) \ F(ReportMessage, 1, 1) \
F(ReThrow, 1, 1) \ F(ReThrow, 1, 1) \
F(RunMicrotaskCallback, 2, 1) \ F(RunMicrotaskCallback, 2, 1) \
......
...@@ -9646,6 +9646,38 @@ TEST(DetachedAccesses) { ...@@ -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 allowed_access = false;
static bool AccessBlocker(Local<v8::Context> accessing_context, static bool AccessBlocker(Local<v8::Context> accessing_context,
Local<v8::Object> accessed_object, Local<v8::Object> accessed_object,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment