mirror of
https://github.com/servo/servo.git
synced 2025-07-24 15:50:21 +01:00
Allow embedder to bypass devtools prompt
This commit is contained in:
parent
3f999ce785
commit
8cf2f14baa
16 changed files with 83 additions and 35 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1208,6 +1208,7 @@ dependencies = [
|
||||||
"msg",
|
"msg",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"servo_rand",
|
||||||
"servo_url",
|
"servo_url",
|
||||||
"time",
|
"time",
|
||||||
"uuid",
|
"uuid",
|
||||||
|
|
|
@ -22,6 +22,7 @@ log = "0.4"
|
||||||
msg = { path = "../msg" }
|
msg = { path = "../msg" }
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
servo_rand = { path = "../rand" }
|
||||||
servo_url = { path = "../url" }
|
servo_url = { path = "../url" }
|
||||||
time = "0.1"
|
time = "0.1"
|
||||||
uuid = { version = "0.8", features = ["v4"] }
|
uuid = { version = "0.8", features = ["v4"] }
|
||||||
|
|
|
@ -39,9 +39,11 @@ use devtools_traits::{PageError, ScriptToDevtoolsControlMsg, WorkerId};
|
||||||
use embedder_traits::{EmbedderMsg, EmbedderProxy, PromptDefinition, PromptOrigin, PromptResult};
|
use embedder_traits::{EmbedderMsg, EmbedderProxy, PromptDefinition, PromptOrigin, PromptResult};
|
||||||
use ipc_channel::ipc::{self, IpcSender};
|
use ipc_channel::ipc::{self, IpcSender};
|
||||||
use msg::constellation_msg::{BrowsingContextId, PipelineId};
|
use msg::constellation_msg::{BrowsingContextId, PipelineId};
|
||||||
|
use servo_rand::RngCore;
|
||||||
use std::borrow::ToOwned;
|
use std::borrow::ToOwned;
|
||||||
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::io::Read;
|
||||||
use std::net::{Shutdown, TcpListener, TcpStream};
|
use std::net::{Shutdown, TcpListener, TcpStream};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
@ -137,8 +139,11 @@ fn run_server(
|
||||||
.map(|port| (l, port))
|
.map(|port| (l, port))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// A token shared with the embedder to bypass permission prompt.
|
||||||
|
let token = format!("{:X}", servo_rand::ServoRng::new().next_u32());
|
||||||
|
|
||||||
let port = bound.as_ref().map(|(_, port)| *port).ok_or(());
|
let port = bound.as_ref().map(|(_, port)| *port).ok_or(());
|
||||||
embedder.send((None, EmbedderMsg::OnDevtoolsStarted(port)));
|
embedder.send((None, EmbedderMsg::OnDevtoolsStarted(port, token.clone())));
|
||||||
|
|
||||||
let listener = match bound {
|
let listener = match bound {
|
||||||
Some((l, _)) => l,
|
Some((l, _)) => l,
|
||||||
|
@ -563,20 +568,14 @@ fn run_server(
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
// accept connections and process them, spawning a new thread for each one
|
// accept connections and process them, spawning a new thread for each one
|
||||||
for stream in listener.incoming() {
|
for stream in listener.incoming() {
|
||||||
// Prompt user for permission
|
let mut stream = stream.expect("Can't retrieve stream");
|
||||||
let (embedder_sender, receiver) =
|
if !allow_devtools_client(&mut stream, &embedder, &token) {
|
||||||
ipc::channel().expect("Failed to create IPC channel!");
|
|
||||||
let message = "Accept incoming devtools connection?".to_owned();
|
|
||||||
let prompt = PromptDefinition::YesNo(message, embedder_sender);
|
|
||||||
let msg = EmbedderMsg::Prompt(prompt, PromptOrigin::Trusted);
|
|
||||||
embedder.send((None, msg));
|
|
||||||
if receiver.recv().unwrap() != PromptResult::Primary {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
};
|
||||||
// connection succeeded and accepted
|
// connection succeeded and accepted
|
||||||
sender
|
sender
|
||||||
.send(DevtoolsControlMsg::FromChrome(
|
.send(DevtoolsControlMsg::FromChrome(
|
||||||
ChromeToDevtoolsControlMsg::AddClient(stream.unwrap()),
|
ChromeToDevtoolsControlMsg::AddClient(stream),
|
||||||
))
|
))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -704,3 +703,33 @@ fn run_server(
|
||||||
let _ = connection.shutdown(Shutdown::Both);
|
let _ = connection.shutdown(Shutdown::Both);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn allow_devtools_client(stream: &mut TcpStream, embedder: &EmbedderProxy, token: &str) -> bool {
|
||||||
|
// By-pass prompt if we receive a valid token.
|
||||||
|
let token = format!("25:{{\"auth_token\":\"{}\"}}", token);
|
||||||
|
let mut buf = [0; 28];
|
||||||
|
let timeout = std::time::Duration::from_millis(500);
|
||||||
|
// This will read but not consume the bytes from the stream.
|
||||||
|
stream.set_read_timeout(Some(timeout)).unwrap();
|
||||||
|
let peek = stream.peek(&mut buf);
|
||||||
|
stream.set_read_timeout(None).unwrap();
|
||||||
|
if let Ok(len) = peek {
|
||||||
|
if len == buf.len() {
|
||||||
|
if let Ok(s) = std::str::from_utf8(&buf) {
|
||||||
|
if s == token {
|
||||||
|
// Consume the message as it was relevant to us.
|
||||||
|
let _ = stream.read_exact(&mut buf);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// No token found. Prompt user
|
||||||
|
let (embedder_sender, receiver) = ipc::channel().expect("Failed to create IPC channel!");
|
||||||
|
let message = "Accept incoming devtools connection?".to_owned();
|
||||||
|
let prompt = PromptDefinition::YesNo(message, embedder_sender);
|
||||||
|
let msg = EmbedderMsg::Prompt(prompt, PromptOrigin::Trusted);
|
||||||
|
embedder.send((None, msg));
|
||||||
|
receiver.recv().unwrap() == PromptResult::Primary
|
||||||
|
}
|
||||||
|
|
|
@ -207,8 +207,8 @@ pub enum EmbedderMsg {
|
||||||
/// Notifies the embedder about media session events
|
/// Notifies the embedder about media session events
|
||||||
/// (i.e. when there is metadata for the active media session, playback state changes...).
|
/// (i.e. when there is metadata for the active media session, playback state changes...).
|
||||||
MediaSessionEvent(MediaSessionEvent),
|
MediaSessionEvent(MediaSessionEvent),
|
||||||
/// Report the status of Devtools Server
|
/// Report the status of Devtools Server with a token that can be used to bypass the permission prompt.
|
||||||
OnDevtoolsStarted(Result<u16, ()>),
|
OnDevtoolsStarted(Result<u16, ()>, String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for EmbedderMsg {
|
impl Debug for EmbedderMsg {
|
||||||
|
|
|
@ -146,7 +146,7 @@ pub trait HostTrait {
|
||||||
/// Called when the media session position state is set.
|
/// Called when the media session position state is set.
|
||||||
fn on_media_session_set_position_state(&self, duration: f64, position: f64, playback_rate: f64);
|
fn on_media_session_set_position_state(&self, duration: f64, position: f64, playback_rate: f64);
|
||||||
/// Called when devtools server is started
|
/// Called when devtools server is started
|
||||||
fn on_devtools_started(&self, port: Result<u16, ()>);
|
fn on_devtools_started(&self, port: Result<u16, ()>, token: String);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ServoGlue {
|
pub struct ServoGlue {
|
||||||
|
@ -751,8 +751,10 @@ impl ServoGlue {
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
EmbedderMsg::OnDevtoolsStarted(port) => {
|
EmbedderMsg::OnDevtoolsStarted(port, token) => {
|
||||||
self.callbacks.host_callbacks.on_devtools_started(port);
|
self.callbacks
|
||||||
|
.host_callbacks
|
||||||
|
.on_devtools_started(port, token);
|
||||||
},
|
},
|
||||||
EmbedderMsg::Status(..) |
|
EmbedderMsg::Status(..) |
|
||||||
EmbedderMsg::SelectFiles(..) |
|
EmbedderMsg::SelectFiles(..) |
|
||||||
|
|
|
@ -227,7 +227,8 @@ pub struct CHostCallbacks {
|
||||||
default: *const c_char,
|
default: *const c_char,
|
||||||
trusted: bool,
|
trusted: bool,
|
||||||
) -> *const c_char,
|
) -> *const c_char,
|
||||||
pub on_devtools_started: extern "C" fn(result: CDevtoolsServerState, port: c_uint),
|
pub on_devtools_started:
|
||||||
|
extern "C" fn(result: CDevtoolsServerState, port: c_uint, token: *const c_char),
|
||||||
pub show_context_menu:
|
pub show_context_menu:
|
||||||
extern "C" fn(title: *const c_char, items_list: *const *const c_char, items_size: u32),
|
extern "C" fn(title: *const c_char, items_list: *const *const c_char, items_size: u32),
|
||||||
pub on_log_output: extern "C" fn(buffer: *const c_char, buffer_length: u32),
|
pub on_log_output: extern "C" fn(buffer: *const c_char, buffer_length: u32),
|
||||||
|
@ -883,15 +884,20 @@ impl HostTrait for HostCallbacks {
|
||||||
Some(contents_str.to_owned())
|
Some(contents_str.to_owned())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_devtools_started(&self, port: Result<u16, ()>) {
|
fn on_devtools_started(&self, port: Result<u16, ()>, token: String) {
|
||||||
|
let token = CString::new(token).expect("Can't create string");
|
||||||
match port {
|
match port {
|
||||||
Ok(p) => {
|
Ok(p) => {
|
||||||
info!("Devtools Server running on port {}", p);
|
info!("Devtools Server running on port {}", p);
|
||||||
(self.0.on_devtools_started)(CDevtoolsServerState::Started, p.into());
|
(self.0.on_devtools_started)(
|
||||||
|
CDevtoolsServerState::Started,
|
||||||
|
p.into(),
|
||||||
|
token.as_ptr(),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
Err(()) => {
|
Err(()) => {
|
||||||
error!("Error running devtools server");
|
error!("Error running devtools server");
|
||||||
(self.0.on_devtools_started)(CDevtoolsServerState::Error, 0);
|
(self.0.on_devtools_started)(CDevtoolsServerState::Error, 0, token.as_ptr());
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -515,7 +515,7 @@ where
|
||||||
debug!("MediaSessionEvent received");
|
debug!("MediaSessionEvent received");
|
||||||
// TODO(ferjm): MediaSession support for winit based browsers.
|
// TODO(ferjm): MediaSession support for winit based browsers.
|
||||||
},
|
},
|
||||||
EmbedderMsg::OnDevtoolsStarted(port) => {
|
EmbedderMsg::OnDevtoolsStarted(port, _token) => {
|
||||||
match port {
|
match port {
|
||||||
Ok(p) => info!("Devtools Server running on port {}", p),
|
Ok(p) => info!("Devtools Server running on port {}", p),
|
||||||
Err(()) => error!("Error running devtools server"),
|
Err(()) => error!("Error running devtools server"),
|
||||||
|
|
|
@ -80,9 +80,10 @@ void BrowserPage::BindServoEvents() {
|
||||||
: Visibility::Visible);
|
: Visibility::Visible);
|
||||||
});
|
});
|
||||||
servoControl().OnDevtoolsStatusChanged(
|
servoControl().OnDevtoolsStatusChanged(
|
||||||
[=](DevtoolsStatus status, unsigned int port) {
|
[=](DevtoolsStatus status, unsigned int port, hstring token) {
|
||||||
mDevtoolsStatus = status;
|
mDevtoolsStatus = status;
|
||||||
mDevtoolsPort = port;
|
mDevtoolsPort = port;
|
||||||
|
mDevtoolsToken = token;
|
||||||
});
|
});
|
||||||
Window::Current().VisibilityChanged(
|
Window::Current().VisibilityChanged(
|
||||||
[=](const auto &, const VisibilityChangedEventArgs &args) {
|
[=](const auto &, const VisibilityChangedEventArgs &args) {
|
||||||
|
@ -318,8 +319,8 @@ void BrowserPage::OnDevtoolsButtonClicked(IInspectable const &,
|
||||||
hstring port = to_hstring(mDevtoolsPort);
|
hstring port = to_hstring(mDevtoolsPort);
|
||||||
if (mDevtoolsClient == nullptr) {
|
if (mDevtoolsClient == nullptr) {
|
||||||
DevtoolsDelegate *dd = static_cast<DevtoolsDelegate *>(this);
|
DevtoolsDelegate *dd = static_cast<DevtoolsDelegate *>(this);
|
||||||
mDevtoolsClient =
|
mDevtoolsClient = std::make_unique<DevtoolsClient>(L"localhost", port,
|
||||||
std::make_unique<DevtoolsClient>(L"localhost", port, *dd);
|
mDevtoolsToken, *dd);
|
||||||
}
|
}
|
||||||
mDevtoolsClient->Run();
|
mDevtoolsClient->Run();
|
||||||
std::wstring message =
|
std::wstring message =
|
||||||
|
|
|
@ -57,6 +57,7 @@ private:
|
||||||
void BuildPrefList();
|
void BuildPrefList();
|
||||||
DevtoolsStatus mDevtoolsStatus = DevtoolsStatus::Stopped;
|
DevtoolsStatus mDevtoolsStatus = DevtoolsStatus::Stopped;
|
||||||
unsigned int mDevtoolsPort = 0;
|
unsigned int mDevtoolsPort = 0;
|
||||||
|
hstring mDevtoolsToken;
|
||||||
std::unique_ptr<servo::DevtoolsClient> mDevtoolsClient;
|
std::unique_ptr<servo::DevtoolsClient> mDevtoolsClient;
|
||||||
Collections::IObservableVector<IInspectable> mLogs;
|
Collections::IObservableVector<IInspectable> mLogs;
|
||||||
};
|
};
|
||||||
|
|
|
@ -31,6 +31,11 @@ void DevtoolsClient::Run() {
|
||||||
connecting.Completed([=](const auto &, const auto &) {
|
connecting.Completed([=](const auto &, const auto &) {
|
||||||
mDataReader = DataReader(socket.InputStream());
|
mDataReader = DataReader(socket.InputStream());
|
||||||
mDataWriter = DataWriter(socket.OutputStream());
|
mDataWriter = DataWriter(socket.OutputStream());
|
||||||
|
|
||||||
|
JsonObject out;
|
||||||
|
out.Insert(L"auth_token", JsonValue::CreateStringValue(mToken));
|
||||||
|
Send(out);
|
||||||
|
|
||||||
mReceiveOp = {Loop()};
|
mReceiveOp = {Loop()};
|
||||||
mReceiveOp->Completed([=](const auto &, const auto &) {
|
mReceiveOp->Completed([=](const auto &, const auto &) {
|
||||||
mReceiveOp = {};
|
mReceiveOp = {};
|
||||||
|
|
|
@ -19,8 +19,9 @@ enum DevtoolsMessageLevel { Error, Warn, None };
|
||||||
class DevtoolsClient {
|
class DevtoolsClient {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DevtoolsClient(hstring hostname, hstring port, DevtoolsDelegate &d)
|
DevtoolsClient(hstring hostname, hstring port, hstring token,
|
||||||
: mDelegate(d), mHostname(hostname), mPort(port){};
|
DevtoolsDelegate &d)
|
||||||
|
: mDelegate(d), mHostname(hostname), mToken(token), mPort(port){};
|
||||||
|
|
||||||
~DevtoolsClient() { Stop(); }
|
~DevtoolsClient() { Stop(); }
|
||||||
void Run();
|
void Run();
|
||||||
|
@ -30,6 +31,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
hstring mPort;
|
hstring mPort;
|
||||||
|
hstring mToken;
|
||||||
hstring mHostname;
|
hstring mHostname;
|
||||||
DevtoolsDelegate &mDelegate;
|
DevtoolsDelegate &mDelegate;
|
||||||
std::optional<DataReader> mDataReader;
|
std::optional<DataReader> mDataReader;
|
||||||
|
|
|
@ -83,9 +83,9 @@ void show_context_menu(const char *title, const char *const *items_list,
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_devtools_started(Servo::DevtoolsServerState result,
|
void on_devtools_started(Servo::DevtoolsServerState result,
|
||||||
const unsigned int port) {
|
const unsigned int port, const char *token) {
|
||||||
sServo->Delegate().OnServoDevtoolsStarted(
|
auto state = result == Servo::DevtoolsServerState::Started;
|
||||||
result == Servo::DevtoolsServerState::Started, port);
|
sServo->Delegate().OnServoDevtoolsStarted(state, port, char2hstring(token));
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_log_output(const char *buffer, uint32_t buffer_length) {
|
void on_log_output(const char *buffer, uint32_t buffer_length) {
|
||||||
|
|
|
@ -108,7 +108,7 @@ public:
|
||||||
virtual bool OnServoAllowNavigation(hstring) = 0;
|
virtual bool OnServoAllowNavigation(hstring) = 0;
|
||||||
virtual void OnServoAnimatingChanged(bool) = 0;
|
virtual void OnServoAnimatingChanged(bool) = 0;
|
||||||
virtual void OnServoIMEStateChanged(bool) = 0;
|
virtual void OnServoIMEStateChanged(bool) = 0;
|
||||||
virtual void OnServoDevtoolsStarted(bool, const unsigned int) = 0;
|
virtual void OnServoDevtoolsStarted(bool, const unsigned int, hstring) = 0;
|
||||||
virtual void OnServoMediaSessionMetadata(hstring, hstring, hstring) = 0;
|
virtual void OnServoMediaSessionMetadata(hstring, hstring, hstring) = 0;
|
||||||
virtual void OnServoMediaSessionPlaybackStateChange(int) = 0;
|
virtual void OnServoMediaSessionPlaybackStateChange(int) = 0;
|
||||||
virtual void OnServoPromptAlert(hstring, bool) = 0;
|
virtual void OnServoPromptAlert(hstring, bool) = 0;
|
||||||
|
|
|
@ -571,11 +571,11 @@ std::optional<hstring> ServoControl::OnServoPromptInput(winrt::hstring message,
|
||||||
return string;
|
return string;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServoControl::OnServoDevtoolsStarted(bool success,
|
void ServoControl::OnServoDevtoolsStarted(bool success, const unsigned int port,
|
||||||
const unsigned int port) {
|
hstring token) {
|
||||||
RunOnUIThread([=] {
|
RunOnUIThread([=] {
|
||||||
auto status = success ? DevtoolsStatus::Running : DevtoolsStatus::Failed;
|
auto status = success ? DevtoolsStatus::Running : DevtoolsStatus::Failed;
|
||||||
mOnDevtoolsStatusChangedEvent(status, port);
|
mOnDevtoolsStatusChangedEvent(status, port, token);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -185,7 +185,7 @@ struct ServoControl : ServoControlT<ServoControl>, public servo::ServoDelegate {
|
||||||
virtual servo::Servo::PromptResult OnServoPromptYesNo(winrt::hstring, bool);
|
virtual servo::Servo::PromptResult OnServoPromptYesNo(winrt::hstring, bool);
|
||||||
virtual std::optional<hstring> OnServoPromptInput(winrt::hstring,
|
virtual std::optional<hstring> OnServoPromptInput(winrt::hstring,
|
||||||
winrt::hstring, bool);
|
winrt::hstring, bool);
|
||||||
virtual void OnServoDevtoolsStarted(bool, const unsigned int);
|
virtual void OnServoDevtoolsStarted(bool, const unsigned int, winrt::hstring);
|
||||||
|
|
||||||
DevtoolsStatus GetDevtoolsStatus();
|
DevtoolsStatus GetDevtoolsStatus();
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ namespace ServoApp {
|
||||||
delegate void EventDelegate();
|
delegate void EventDelegate();
|
||||||
delegate void HistoryChangedDelegate(Boolean back, Boolean forward);
|
delegate void HistoryChangedDelegate(Boolean back, Boolean forward);
|
||||||
delegate void MediaSessionMetadataDelegate(String title, String artist, String album);
|
delegate void MediaSessionMetadataDelegate(String title, String artist, String album);
|
||||||
delegate void DevtoolsStatusChangedDelegate(DevtoolsStatus status, UInt32 port);
|
delegate void DevtoolsStatusChangedDelegate(DevtoolsStatus status, UInt32 port, String token);
|
||||||
|
|
||||||
enum DevtoolsStatus {
|
enum DevtoolsStatus {
|
||||||
Running = 0,
|
Running = 0,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue