Skip to content

Commit fb94879

Browse files
committed
Reapply "[RUN-2863] finish devtools plumbing (#1032)" (#1078)
This reverts commit 971ced2.
1 parent cec830d commit fb94879

File tree

14 files changed

+329
-192
lines changed

14 files changed

+329
-192
lines changed

components/record_replay/services/auth_token/public/cpp/auth_token_service.cc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,26 @@ void RecordReplayAuthTokenService::BindAuthTokenStore(
1818

1919
void RecordReplayAuthTokenService::SetToken(const std::string& token) {
2020
token_ = token;
21+
// TODO persist the token to browser prefs
2122
NotifyObservers();
2223
}
2324

25+
void RecordReplayAuthTokenService::ClearToken() {
26+
token_.clear(); // not sure about this.. should we be sending a null?
27+
NotifyObservers();
28+
}
29+
30+
void RecordReplayAuthTokenService::SetUser(const std::string& user) {
31+
// TODO persist the user to browser prefs
32+
}
33+
34+
void RecordReplayAuthTokenService::ClearUser() {
35+
// TODO clear the user from browser prefs
36+
}
37+
38+
void RecordReplayAuthTokenService::Login() {
39+
}
40+
2441
void RecordReplayAuthTokenService::AddObserver(mojo::PendingRemote<mojom::RecordReplayAuthTokenStoreObserver> observer) {
2542
observers_.Add(std::move(observer));
2643
NotifyObservers();

components/record_replay/services/auth_token/public/cpp/auth_token_service.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ class RecordReplayAuthTokenService : public KeyedService, public mojom::RecordRe
2727

2828
// mojom::RecordReplayAuthTokenStore:
2929
void SetToken(const std::string& token) override;
30+
void ClearToken() override;
31+
32+
void SetUser(const std::string& user) override;
33+
void ClearUser() override;
34+
35+
void Login() override;
36+
3037
void AddObserver(mojo::PendingRemote<mojom::RecordReplayAuthTokenStoreObserver> observer) override;
3138

3239
private:

components/record_replay/services/auth_token/public/mojom/auth_token.mojom

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@ module auth_token.mojom;
33
// Interface to passing updated token around
44
interface RecordReplayAuthTokenStore {
55
SetToken(string token);
6+
ClearToken();
7+
8+
SetUser (string user);
9+
ClearUser();
10+
11+
// Opens an external browser to log the user in
12+
Login();
613

714
AddObserver(pending_remote<RecordReplayAuthTokenStoreObserver> observer);
815
// how do we remove?

third_party/blink/renderer/bindings/bindings.gni

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,12 @@ blink_core_sources_bindings =
6868
"core/v8/observable_array_exotic_object_impl.h",
6969
"core/v8/profiler_trace_builder.cc",
7070
"core/v8/profiler_trace_builder.h",
71-
"core/v8/record_replay_interface.cc",
72-
"core/v8/record_replay_interface.h",
71+
"core/v8/record_replay_devtools_event_listener.cc",
72+
"core/v8/record_replay_devtools_event_listener.h",
7373
"core/v8/record_replay_events.cc",
7474
"core/v8/record_replay_events.h",
75+
"core/v8/record_replay_interface.cc",
76+
"core/v8/record_replay_interface.h",
7577
"core/v8/record_replay_network.cc",
7678
"core/v8/record_replay_network.h",
7779
"core/v8/referrer_script_info.cc",

third_party/blink/renderer/bindings/core/v8/local_window_proxy.cc

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include "base/metrics/single_sample_metrics.h"
3838
#include "third_party/blink/renderer/bindings/core/v8/isolated_world_csp.h"
3939
#include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
40+
#include "third_party/blink/renderer/bindings/core/v8/record_replay_devtools_event_listener.h"
4041
#include "third_party/blink/renderer/bindings/core/v8/record_replay_interface.h"
4142
#include "third_party/blink/renderer/bindings/core/v8/to_v8_for_core.h"
4243
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
@@ -268,17 +269,17 @@ void LocalWindowProxy::Initialize() {
268269

269270
// Add an event listener for the dispatched custom event the devtools uses to register
270271
// its listener. Do this outside the recording.
271-
SetupRecordReplayEventListener();
272+
SetupRecordReplayDevtoolsEventListener();
272273

273274
if (World().IsMainWorld()) {
274275
GetFrame()->Loader().DispatchDidClearWindowObjectInMainWorld();
275276
}
276277
}
277278

278-
void LocalWindowProxy::SetupRecordReplayEventListener() {
279+
void LocalWindowProxy::SetupRecordReplayDevtoolsEventListener() {
279280
LocalFrame* localFrame = GetFrame();
280281

281-
record_replay_listener_ = RecordReplayEventListener::Create(GetIsolate(), localFrame);
282+
record_replay_listener_ = RecordReplayDevtoolsEventListener::Create(GetIsolate(), localFrame);
282283

283284
bool added = localFrame->DomWindow()->addEventListener("WebChannelMessageToChrome", record_replay_listener_.Get());
284285

third_party/blink/renderer/bindings/core/v8/local_window_proxy.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ namespace blink {
4646
class HTMLDocument;
4747
class ScriptState;
4848
class SecurityOrigin;
49-
class RecordReplayEventListener;
49+
class RecordReplayDevtoolsEventListener;
5050

5151
// Subclass of WindowProxy that only handles LocalFrame.
5252
class LocalWindowProxy final : public WindowProxy {
@@ -73,7 +73,7 @@ class LocalWindowProxy final : public WindowProxy {
7373
v8::Context::AbortScriptExecutionCallback callback);
7474

7575
private:
76-
void SetupRecordReplayEventListener();
76+
void SetupRecordReplayDevtoolsEventListener();
7777

7878
// LocalWindowProxy overrides:
7979
bool IsLocal() const override { return true; }
@@ -116,7 +116,7 @@ class LocalWindowProxy final : public WindowProxy {
116116

117117
mojo::Remote<auth_token::mojom::RecordReplayAuthTokenStore> auth_token_store_;
118118

119-
Member<RecordReplayEventListener> record_replay_listener_;
119+
Member<RecordReplayDevtoolsEventListener> record_replay_listener_;
120120
Member<ScriptState> script_state_;
121121
bool context_was_created_from_snapshot_ = false;
122122
};
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
// Copyright 2021 Record Replay Inc. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include "third_party/blink/renderer/bindings/core/v8/record_replay_devtools_event_listener.h"
6+
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
7+
#include "third_party/blink/renderer/core/dom/events/custom_event.h"
8+
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
9+
10+
namespace blink {
11+
12+
static v8::Local<v8::String> ToV8String(v8::Isolate* isolate, const char* value) {
13+
return v8::String::NewFromUtf8(isolate, value,
14+
v8::NewStringType::kInternalized).ToLocalChecked();
15+
}
16+
static const std::string V8ToString(v8::Isolate* isolate, v8::Local<v8::Value> str) {
17+
v8::String::Utf8Value s(isolate, str);
18+
return *s;
19+
}
20+
21+
static bool GetStringProperty(v8::Local<v8::Context> context, v8::Local<v8::Object> obj, const char* name, v8::Local<v8::String>* out) {
22+
v8::Isolate* isolate = context->GetIsolate();
23+
v8::Local<v8::String> v8Name = ToV8String(isolate, name);
24+
v8::Local<v8::Value> v8Value = obj->Get(context, v8Name).ToLocalChecked();
25+
26+
return v8Value->ToString(context).ToLocal(out);
27+
}
28+
29+
static bool GetObjectProperty(v8::Local<v8::Context> context, v8::Local<v8::Object> obj, const char* name, v8::Local<v8::Object>* out) {
30+
v8::Isolate* isolate = context->GetIsolate();
31+
v8::Local<v8::String> v8Name = ToV8String(isolate, name);
32+
v8::Local<v8::Value> v8Value = obj->Get(context, v8Name).ToLocalChecked();
33+
34+
return v8Value->ToObject(context).ToLocal(out);
35+
}
36+
37+
static bool StringEquals(v8::Isolate* isolate, v8::Local<v8::String> str1, const char* str2) {
38+
return str1->StringEquals(ToV8String(isolate, str2));
39+
}
40+
41+
42+
void RecordReplayDevtoolsEventListener::Invoke(ExecutionContext* context, Event* event) {
43+
v8::Isolate* isolate = context->GetIsolate();
44+
v8::Local<v8::Context> v8_context = isolate->GetCurrentContext();
45+
ScriptState* scriptState = ScriptState::Current(isolate);
46+
CustomEvent* customEvent = To<CustomEvent>(event);
47+
48+
if (!customEvent) {
49+
return;
50+
}
51+
52+
v8::Local<v8::Value> detail = customEvent->detail(scriptState).V8Value();
53+
v8::Local<v8::String> detail_json;
54+
if (!detail->ToString(v8_context).ToLocal(&detail_json)) {
55+
LOG(ERROR) << "[RUN-2863] RecordReplayDevtoolsEventListener: detail is not a string";
56+
return;
57+
}
58+
59+
// for debugging:
60+
// LOG(ERROR) << "RecordReplayDevtoolsEventListener: detail = " << V8ToString(isolate, detail_json);
61+
62+
// detail is a JSON stringified object with one of the following forms:
63+
64+
// { "id": "record-replay-token", "message": { "type": "connect" } } => register auth token observer
65+
// { "id": "record-replay-token", "message": { "type": "login" } } => open external browser to login
66+
// { "id": "record-replay-token", "message": { "token": <string|null> } } => set access token if string. clear if null (or undefined?)
67+
// { "id": "record-replay", "message": { "user": <string|null> } } => set user if string. clear if null (or undefined?)
68+
69+
v8::Local<v8::Object> detail_obj;
70+
if (!v8::JSON::Parse(v8_context, detail_json).ToLocalChecked()->ToObject(v8_context).ToLocal(&detail_obj)) {
71+
LOG(ERROR) << "[RUN-2863] RecordReplayDevtoolsEventListener: detail is not a JSON object";
72+
return;
73+
}
74+
75+
// always pull out the id and message properties, and early out if id isn't a string or message isn't an object
76+
v8::Local<v8::String> id_str;
77+
if (!GetStringProperty(v8_context, detail_obj, "id", &id_str)) {
78+
LOG(ERROR) << "[RUN-2863] RecordReplayDevtoolsEventListener: id is not an string";
79+
return;
80+
}
81+
82+
v8::Local<v8::Object> message_obj;
83+
if (!GetObjectProperty(v8_context, detail_obj, "message", &message_obj)) {
84+
LOG(ERROR) << "[RUN-2863] RecordReplayDevtoolsEventListener: message is not an object";
85+
return;
86+
}
87+
88+
89+
if (StringEquals(isolate, id_str, "record-replay-token")) {
90+
HandleRecordReplayTokenMessage(v8_context, message_obj);
91+
} else if (StringEquals(isolate, id_str, "record-replay")) {
92+
HandleRecordReplayMessage(v8_context, message_obj);
93+
} else {
94+
LOG(ERROR) << "[RUN-2863] Unknown event id: " << V8ToString(isolate, id_str);
95+
}
96+
}
97+
98+
void RecordReplayDevtoolsEventListener::HandleRecordReplayTokenMessage(v8::Local<v8::Context> context, v8::Local<v8::Object> message) {
99+
v8::Isolate* isolate = context->GetIsolate();
100+
101+
// cases here:
102+
// { "id": "record-replay-token", "message": { "type": "connect" } } => register auth token observer
103+
// { "id": "record-replay-token", "message": { "type": "login" } } => open external browser to login
104+
// { "id": "record-replay-token", "message": { "token": <string|null> } } => set access token if string. clear if null (or undefined?)
105+
106+
// first check if there's a type property to handle the first two cases above.
107+
v8::Local<v8::Value> message_type = message->Get(context, ToV8String(isolate, "type")).ToLocalChecked();
108+
if (message_type->IsString()) {
109+
// message is either `{ type: "connect" }` or `{ type: "login" }`, with neither payload carrying additional info.
110+
if (StringEquals(isolate, message_type.As<v8::String>(), "connect")) {
111+
LOG(ERROR) << "[RUN-2863] RecordReplayDevtoolsEventListener: connect message received";
112+
local_frame_->RecordReplayRegisterAuthTokenObserver();
113+
return;
114+
}
115+
116+
if (StringEquals(isolate, message_type.As<v8::String>(), "login")) {
117+
LOG(ERROR) << "[RUN-2863] RecordReplayDevtoolsEventListener: login message received";
118+
local_frame_->RecordReplayLogin();
119+
return;
120+
}
121+
122+
LOG(ERROR) << "[RUN-2863] RecordReplayDevtoolsEventListener: unknown record-replay-token message type: " << V8ToString(isolate, message_type);
123+
}
124+
125+
// if we're here, we should only be in the `{ token: ... }` case from the list above.
126+
v8::Local<v8::Value> message_token = message->Get(context, ToV8String(isolate, "token")).ToLocalChecked();
127+
if (message_token->IsString()) {
128+
LOG(ERROR) << "[RUN-2863] RecordReplayDevtoolsEventListener: set access token message received, token = " << V8ToString(isolate, message_token);
129+
local_frame_->RecordReplaySetToken(ToCoreString(message_token.As<v8::String>()));
130+
return;
131+
}
132+
133+
if (message_token->IsNullOrUndefined()) {
134+
LOG(ERROR) << "[RUN-2863] RecordReplayDevtoolsEventListener: clear access token message received";
135+
local_frame_->RecordReplayClearToken();
136+
return;
137+
}
138+
139+
LOG(ERROR) << "[RUN-2863] RecordReplayDevtoolsEventListener: unknown record-replay-token message";
140+
}
141+
142+
void RecordReplayDevtoolsEventListener::HandleRecordReplayMessage(v8::Local<v8::Context> context, v8::Local<v8::Object> message) {
143+
v8::Isolate* isolate = context->GetIsolate();
144+
145+
// the only message handled here is `{ user: <string|null> }`
146+
v8::Local<v8::Value> message_user = message->Get(context, ToV8String(isolate, "user")).ToLocalChecked();
147+
if (message_user->IsString()) {
148+
LOG(ERROR) << "[RUN-2863] RecordReplayDevtoolsEventListener: set user message received, user = " << V8ToString(isolate, message_user);
149+
local_frame_->RecordReplaySetUser(ToCoreString(message_user.As<v8::String>()));
150+
return;
151+
}
152+
153+
if (message_user->IsNullOrUndefined()) {
154+
LOG(ERROR) << "[RUN-2863] RecordReplayDevtoolsEventListener: clear user message received";
155+
local_frame_->RecordReplayClearUser();
156+
return;
157+
}
158+
159+
LOG(ERROR) << "[RUN-2863] Unknown record-replay message type";
160+
return;
161+
}
162+
163+
void RecordReplayDevtoolsEventListener::Trace(Visitor* visitor) const {
164+
visitor->Trace(local_frame_);
165+
EventListener::Trace(visitor);
166+
}
167+
168+
} // namespace blink
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright 2021 Record Replay Inc. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#ifndef THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_RECORD_REPLAY_DEVTOOLS_EVENT_LISTENER_H_
6+
#define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_RECORD_REPLAY_DEVTOOLS_EVENT_LISTENER_H_
7+
8+
#include "third_party/blink/renderer/core/dom/events/native_event_listener.h"
9+
#include "third_party/blink/renderer/core/frame/local_frame.h"
10+
#include "v8/include/v8.h"
11+
12+
namespace blink {
13+
14+
class RecordReplayDevtoolsEventListener : public NativeEventListener {
15+
public:
16+
RecordReplayDevtoolsEventListener(v8::Isolate* isolate, LocalFrame* localFrame)
17+
: local_frame_(localFrame) {}
18+
19+
void Invoke(ExecutionContext*, Event*) override;
20+
21+
void Trace(Visitor*) const override;
22+
23+
static RecordReplayDevtoolsEventListener* Create(v8::Isolate* isolate,
24+
LocalFrame* localFrame) {
25+
return MakeGarbageCollected<RecordReplayDevtoolsEventListener>(isolate, localFrame);
26+
}
27+
28+
private:
29+
Member<LocalFrame> local_frame_;
30+
31+
void HandleRecordReplayTokenMessage(v8::Local<v8::Context> context, v8::Local<v8::Object> message);
32+
void HandleRecordReplayMessage(v8::Local<v8::Context> context, v8::Local<v8::Object> message);
33+
};
34+
35+
} // namespace blink
36+
37+
#endif // THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_RECORD_REPLAY_DEVTOOLS_EVENT_LISTENER_H_

0 commit comments

Comments
 (0)