Allow embedder to bypass devtools prompt

This commit is contained in:
Paul Rouget 2020-06-22 11:06:34 +02:00
parent 3f999ce785
commit 8cf2f14baa
16 changed files with 83 additions and 35 deletions

1
Cargo.lock generated
View file

@ -1208,6 +1208,7 @@ dependencies = [
"msg", "msg",
"serde", "serde",
"serde_json", "serde_json",
"servo_rand",
"servo_url", "servo_url",
"time", "time",
"uuid", "uuid",

View file

@ -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"] }

View file

@ -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
}

View file

@ -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 {

View file

@ -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(..) |

View file

@ -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());
}, },
} }
} }

View file

@ -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"),

View file

@ -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 =

View file

@ -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;
}; };

View file

@ -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 = {};

View file

@ -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;

View file

@ -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) {

View file

@ -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;

View file

@ -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);
}); });
} }

View file

@ -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();

View file

@ -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,