Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow embedder to bypass devtools prompt #27025

Merged
merged 2 commits into from Jun 22, 2020
Merged
Changes from 1 commit
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

Next

Allow embedder to bypass devtools prompt

  • Loading branch information
paulrouget committed Jun 22, 2020
commit 8cf2f14baa6b5235be70b854474bdb48dd9bbae1

Some generated files are not rendered by default. Learn more.

@@ -22,6 +22,7 @@ log = "0.4"
msg = { path = "../msg" }
serde = "1.0"
serde_json = "1.0"
servo_rand = { path = "../rand" }
servo_url = { path = "../url" }
time = "0.1"
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 ipc_channel::ipc::{self, IpcSender};
use msg::constellation_msg::{BrowsingContextId, PipelineId};
use servo_rand::RngCore;
use std::borrow::ToOwned;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::HashMap;
use std::io::Read;
use std::net::{Shutdown, TcpListener, TcpStream};
use std::sync::{Arc, Mutex};
use std::thread;
@@ -137,8 +139,11 @@ fn run_server(
.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(());
embedder.send((None, EmbedderMsg::OnDevtoolsStarted(port)));
embedder.send((None, EmbedderMsg::OnDevtoolsStarted(port, token.clone())));

let listener = match bound {
Some((l, _)) => l,
@@ -563,20 +568,14 @@ fn run_server(
.spawn(move || {
// accept connections and process them, spawning a new thread for each one
for stream in listener.incoming() {
// Prompt user for permission
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));
if receiver.recv().unwrap() != PromptResult::Primary {
let mut stream = stream.expect("Can't retrieve stream");
if !allow_devtools_client(&mut stream, &embedder, &token) {
continue;
}
};
// connection succeeded and accepted
sender
.send(DevtoolsControlMsg::FromChrome(
ChromeToDevtoolsControlMsg::AddClient(stream.unwrap()),
ChromeToDevtoolsControlMsg::AddClient(stream),
))
.unwrap();
}
@@ -704,3 +703,33 @@ fn run_server(
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
/// (i.e. when there is metadata for the active media session, playback state changes...).
MediaSessionEvent(MediaSessionEvent),
/// Report the status of Devtools Server
OnDevtoolsStarted(Result<u16, ()>),
/// Report the status of Devtools Server with a token that can be used to bypass the permission prompt.
OnDevtoolsStarted(Result<u16, ()>, String),
}

impl Debug for EmbedderMsg {
@@ -146,7 +146,7 @@ pub trait HostTrait {
/// Called when the media session position state is set.
fn on_media_session_set_position_state(&self, duration: f64, position: f64, playback_rate: f64);
/// 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 {
@@ -751,8 +751,10 @@ impl ServoGlue {
),
};
},
EmbedderMsg::OnDevtoolsStarted(port) => {
self.callbacks.host_callbacks.on_devtools_started(port);
EmbedderMsg::OnDevtoolsStarted(port, token) => {
self.callbacks
.host_callbacks
.on_devtools_started(port, token);
},
EmbedderMsg::Status(..) |
EmbedderMsg::SelectFiles(..) |
@@ -227,7 +227,8 @@ pub struct CHostCallbacks {
default: *const c_char,
trusted: bool,
) -> *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:
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),
@@ -883,15 +884,20 @@ impl HostTrait for HostCallbacks {
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 {
Ok(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(()) => {
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");
// TODO(ferjm): MediaSession support for winit based browsers.
},
EmbedderMsg::OnDevtoolsStarted(port) => {
EmbedderMsg::OnDevtoolsStarted(port, _token) => {
match port {
Ok(p) => info!("Devtools Server running on port {}", p),
Err(()) => error!("Error running devtools server"),
@@ -80,9 +80,10 @@ void BrowserPage::BindServoEvents() {
: Visibility::Visible);
});
servoControl().OnDevtoolsStatusChanged(
[=](DevtoolsStatus status, unsigned int port) {
[=](DevtoolsStatus status, unsigned int port, hstring token) {
mDevtoolsStatus = status;
mDevtoolsPort = port;
mDevtoolsToken = token;
});
Window::Current().VisibilityChanged(
[=](const auto &, const VisibilityChangedEventArgs &args) {
@@ -318,8 +319,8 @@ void BrowserPage::OnDevtoolsButtonClicked(IInspectable const &,
hstring port = to_hstring(mDevtoolsPort);
if (mDevtoolsClient == nullptr) {
DevtoolsDelegate *dd = static_cast<DevtoolsDelegate *>(this);
mDevtoolsClient =
std::make_unique<DevtoolsClient>(L"localhost", port, *dd);
mDevtoolsClient = std::make_unique<DevtoolsClient>(L"localhost", port,
mDevtoolsToken, *dd);
}
mDevtoolsClient->Run();
std::wstring message =
@@ -57,6 +57,7 @@ struct BrowserPage : BrowserPageT<BrowserPage>, public servo::DevtoolsDelegate {
void BuildPrefList();
DevtoolsStatus mDevtoolsStatus = DevtoolsStatus::Stopped;
unsigned int mDevtoolsPort = 0;
hstring mDevtoolsToken;
std::unique_ptr<servo::DevtoolsClient> mDevtoolsClient;
Collections::IObservableVector<IInspectable> mLogs;
};
@@ -31,6 +31,11 @@ void DevtoolsClient::Run() {
connecting.Completed([=](const auto &, const auto &) {
mDataReader = DataReader(socket.InputStream());
mDataWriter = DataWriter(socket.OutputStream());

JsonObject out;
out.Insert(L"auth_token", JsonValue::CreateStringValue(mToken));
Send(out);

mReceiveOp = {Loop()};
mReceiveOp->Completed([=](const auto &, const auto &) {
mReceiveOp = {};
@@ -19,8 +19,9 @@ enum DevtoolsMessageLevel { Error, Warn, None };
class DevtoolsClient {

public:
DevtoolsClient(hstring hostname, hstring port, DevtoolsDelegate &d)
: mDelegate(d), mHostname(hostname), mPort(port){};
DevtoolsClient(hstring hostname, hstring port, hstring token,
DevtoolsDelegate &d)
: mDelegate(d), mHostname(hostname), mToken(token), mPort(port){};

~DevtoolsClient() { Stop(); }
void Run();
@@ -30,6 +31,7 @@ class DevtoolsClient {

private:
hstring mPort;
hstring mToken;
hstring mHostname;
DevtoolsDelegate &mDelegate;
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,
const unsigned int port) {
sServo->Delegate().OnServoDevtoolsStarted(
result == Servo::DevtoolsServerState::Started, port);
const unsigned int port, const char *token) {
auto state = result == Servo::DevtoolsServerState::Started;
sServo->Delegate().OnServoDevtoolsStarted(state, port, char2hstring(token));
}

void on_log_output(const char *buffer, uint32_t buffer_length) {
@@ -108,7 +108,7 @@ class ServoDelegate {
virtual bool OnServoAllowNavigation(hstring) = 0;
virtual void OnServoAnimatingChanged(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 OnServoMediaSessionPlaybackStateChange(int) = 0;
virtual void OnServoPromptAlert(hstring, bool) = 0;
@@ -571,11 +571,11 @@ std::optional<hstring> ServoControl::OnServoPromptInput(winrt::hstring message,
return string;
}

void ServoControl::OnServoDevtoolsStarted(bool success,
const unsigned int port) {
void ServoControl::OnServoDevtoolsStarted(bool success, const unsigned int port,
hstring token) {
RunOnUIThread([=] {
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 std::optional<hstring> OnServoPromptInput(winrt::hstring,
winrt::hstring, bool);
virtual void OnServoDevtoolsStarted(bool, const unsigned int);
virtual void OnServoDevtoolsStarted(bool, const unsigned int, winrt::hstring);

DevtoolsStatus GetDevtoolsStatus();

@@ -3,7 +3,7 @@ namespace ServoApp {
delegate void EventDelegate();
delegate void HistoryChangedDelegate(Boolean back, Boolean forward);
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 {
Running = 0,
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.