mirror of
https://github.com/servo/servo.git
synced 2025-10-12 06:20:26 +01:00
This fixes many rustdoc errors that occur due to raw URLs in rustdoc comments as well as unescaped Rust code that should be in backticks.
386 lines
12 KiB
Rust
386 lines
12 KiB
Rust
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
|
|
//! Liberally derived from the [Firefox JS implementation](http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/webbrowser.js).
|
|
//! Connection point for remote devtools that wish to investigate a particular Browsing Context's contents.
|
|
//! Supports dynamic attaching and detaching which control notifications of navigation, etc.
|
|
|
|
use std::cell::{Cell, RefCell};
|
|
use std::collections::HashMap;
|
|
use std::net::TcpStream;
|
|
|
|
use devtools_traits::DevtoolScriptControlMsg::{self, WantsLiveNotifications};
|
|
use devtools_traits::{DevtoolsPageInfo, NavigationState};
|
|
use ipc_channel::ipc::IpcSender;
|
|
use msg::constellation_msg::{BrowsingContextId, PipelineId};
|
|
use serde::Serialize;
|
|
use serde_json::{Map, Value};
|
|
|
|
use crate::actor::{Actor, ActorMessageStatus, ActorRegistry};
|
|
use crate::actors::emulation::EmulationActor;
|
|
use crate::actors::inspector::InspectorActor;
|
|
use crate::actors::performance::PerformanceActor;
|
|
use crate::actors::profiler::ProfilerActor;
|
|
use crate::actors::stylesheets::StyleSheetsActor;
|
|
use crate::actors::tab::TabDescriptorActor;
|
|
use crate::actors::thread::ThreadActor;
|
|
use crate::actors::timeline::TimelineActor;
|
|
use crate::protocol::JsonPacketStream;
|
|
use crate::StreamId;
|
|
|
|
#[derive(Serialize)]
|
|
struct BrowsingContextTraits {
|
|
isBrowsingContext: bool,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
struct AttachedTraits {
|
|
reconfigure: bool,
|
|
frames: bool,
|
|
logInPage: bool,
|
|
canRewind: bool,
|
|
watchpoints: bool,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
struct BrowsingContextAttachedReply {
|
|
from: String,
|
|
#[serde(rename = "type")]
|
|
type_: String,
|
|
threadActor: String,
|
|
cacheDisabled: bool,
|
|
javascriptEnabled: bool,
|
|
traits: AttachedTraits,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
struct BrowsingContextDetachedReply {
|
|
from: String,
|
|
#[serde(rename = "type")]
|
|
type_: String,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
struct ReconfigureReply {
|
|
from: String,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
struct ListFramesReply {
|
|
from: String,
|
|
frames: Vec<FrameMsg>,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
struct FrameMsg {
|
|
id: u32,
|
|
url: String,
|
|
title: String,
|
|
parentID: u32,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
struct ListWorkersReply {
|
|
from: String,
|
|
workers: Vec<WorkerMsg>,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
struct WorkerMsg {
|
|
id: u32,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
pub struct BrowsingContextActorMsg {
|
|
actor: String,
|
|
title: String,
|
|
url: String,
|
|
outerWindowID: u32,
|
|
browsingContextId: u32,
|
|
consoleActor: String,
|
|
/*emulationActor: String,
|
|
inspectorActor: String,
|
|
timelineActor: String,
|
|
profilerActor: String,
|
|
performanceActor: String,
|
|
styleSheetsActor: String,*/
|
|
traits: BrowsingContextTraits,
|
|
// Part of the official protocol, but not yet implemented.
|
|
/*storageActor: String,
|
|
memoryActor: String,
|
|
framerateActor: String,
|
|
reflowActor: String,
|
|
cssPropertiesActor: String,
|
|
animationsActor: String,
|
|
webExtensionInspectedWindowActor: String,
|
|
accessibilityActor: String,
|
|
screenshotActor: String,
|
|
changesActor: String,
|
|
webSocketActor: String,
|
|
manifestActor: String,*/
|
|
}
|
|
|
|
pub(crate) struct BrowsingContextActor {
|
|
pub name: String,
|
|
pub title: RefCell<String>,
|
|
pub url: RefCell<String>,
|
|
pub console: String,
|
|
pub _emulation: String,
|
|
pub _inspector: String,
|
|
pub _timeline: String,
|
|
pub _profiler: String,
|
|
pub _performance: String,
|
|
pub _styleSheets: String,
|
|
pub thread: String,
|
|
pub _tab: String,
|
|
pub streams: RefCell<HashMap<StreamId, TcpStream>>,
|
|
pub browsing_context_id: BrowsingContextId,
|
|
pub active_pipeline: Cell<PipelineId>,
|
|
pub script_chan: IpcSender<DevtoolScriptControlMsg>,
|
|
}
|
|
|
|
impl Actor for BrowsingContextActor {
|
|
fn name(&self) -> String {
|
|
self.name.clone()
|
|
}
|
|
|
|
fn handle_message(
|
|
&self,
|
|
_registry: &ActorRegistry,
|
|
msg_type: &str,
|
|
msg: &Map<String, Value>,
|
|
stream: &mut TcpStream,
|
|
id: StreamId,
|
|
) -> Result<ActorMessageStatus, ()> {
|
|
Ok(match msg_type {
|
|
"reconfigure" => {
|
|
if let Some(options) = msg.get("options").and_then(|o| o.as_object()) {
|
|
if let Some(val) = options.get("performReload") {
|
|
if val.as_bool().unwrap_or(false) {
|
|
let _ = self
|
|
.script_chan
|
|
.send(DevtoolScriptControlMsg::Reload(self.active_pipeline.get()));
|
|
}
|
|
}
|
|
}
|
|
let _ = stream.write_json_packet(&ReconfigureReply { from: self.name() });
|
|
ActorMessageStatus::Processed
|
|
},
|
|
|
|
// https://docs.firefox-dev.tools/backend/protocol.html#listing-browser-tabs
|
|
// (see "To attach to a _targetActor_")
|
|
"attach" => {
|
|
let msg = BrowsingContextAttachedReply {
|
|
from: self.name(),
|
|
type_: "tabAttached".to_owned(),
|
|
threadActor: self.thread.clone(),
|
|
cacheDisabled: false,
|
|
javascriptEnabled: true,
|
|
traits: AttachedTraits {
|
|
reconfigure: false,
|
|
frames: true,
|
|
logInPage: false,
|
|
canRewind: false,
|
|
watchpoints: false,
|
|
},
|
|
};
|
|
|
|
if stream.write_json_packet(&msg).is_err() {
|
|
return Ok(ActorMessageStatus::Processed);
|
|
}
|
|
self.streams
|
|
.borrow_mut()
|
|
.insert(id, stream.try_clone().unwrap());
|
|
self.script_chan
|
|
.send(WantsLiveNotifications(self.active_pipeline.get(), true))
|
|
.unwrap();
|
|
ActorMessageStatus::Processed
|
|
},
|
|
|
|
"detach" => {
|
|
let msg = BrowsingContextDetachedReply {
|
|
from: self.name(),
|
|
type_: "detached".to_owned(),
|
|
};
|
|
let _ = stream.write_json_packet(&msg);
|
|
self.cleanup(id);
|
|
ActorMessageStatus::Processed
|
|
},
|
|
|
|
"listFrames" => {
|
|
let msg = ListFramesReply {
|
|
from: self.name(),
|
|
frames: vec![FrameMsg {
|
|
//FIXME: shouldn't ignore pipeline namespace field
|
|
id: self.active_pipeline.get().index.0.get(),
|
|
parentID: 0,
|
|
url: self.url.borrow().clone(),
|
|
title: self.title.borrow().clone(),
|
|
}],
|
|
};
|
|
let _ = stream.write_json_packet(&msg);
|
|
ActorMessageStatus::Processed
|
|
},
|
|
|
|
"listWorkers" => {
|
|
let msg = ListWorkersReply {
|
|
from: self.name(),
|
|
workers: vec![],
|
|
};
|
|
let _ = stream.write_json_packet(&msg);
|
|
ActorMessageStatus::Processed
|
|
},
|
|
|
|
_ => ActorMessageStatus::Ignored,
|
|
})
|
|
}
|
|
|
|
fn cleanup(&self, id: StreamId) {
|
|
self.streams.borrow_mut().remove(&id);
|
|
if self.streams.borrow().is_empty() {
|
|
self.script_chan
|
|
.send(WantsLiveNotifications(self.active_pipeline.get(), false))
|
|
.unwrap();
|
|
}
|
|
}
|
|
}
|
|
|
|
impl BrowsingContextActor {
|
|
pub(crate) fn new(
|
|
console: String,
|
|
id: BrowsingContextId,
|
|
page_info: DevtoolsPageInfo,
|
|
pipeline: PipelineId,
|
|
script_sender: IpcSender<DevtoolScriptControlMsg>,
|
|
actors: &mut ActorRegistry,
|
|
) -> BrowsingContextActor {
|
|
let emulation = EmulationActor::new(actors.new_name("emulation"));
|
|
|
|
let name = actors.new_name("target");
|
|
|
|
let inspector = InspectorActor {
|
|
name: actors.new_name("inspector"),
|
|
walker: RefCell::new(None),
|
|
pageStyle: RefCell::new(None),
|
|
highlighter: RefCell::new(None),
|
|
script_chan: script_sender.clone(),
|
|
browsing_context: name.clone(),
|
|
};
|
|
|
|
let timeline =
|
|
TimelineActor::new(actors.new_name("timeline"), pipeline, script_sender.clone());
|
|
|
|
let profiler = ProfilerActor::new(actors.new_name("profiler"));
|
|
let performance = PerformanceActor::new(actors.new_name("performance"));
|
|
|
|
// the strange switch between styleSheets and stylesheets is due
|
|
// to an inconsistency in devtools. See Bug #1498893 in bugzilla
|
|
let styleSheets = StyleSheetsActor::new(actors.new_name("stylesheets"));
|
|
let thread = ThreadActor::new(actors.new_name("context"));
|
|
|
|
let DevtoolsPageInfo { title, url } = page_info;
|
|
|
|
let tabdesc = TabDescriptorActor::new(actors, name.clone());
|
|
|
|
let target = BrowsingContextActor {
|
|
name: name,
|
|
script_chan: script_sender,
|
|
title: RefCell::new(String::from(title)),
|
|
url: RefCell::new(url.into_string()),
|
|
console: console,
|
|
_emulation: emulation.name(),
|
|
_inspector: inspector.name(),
|
|
_timeline: timeline.name(),
|
|
_profiler: profiler.name(),
|
|
_performance: performance.name(),
|
|
_styleSheets: styleSheets.name(),
|
|
_tab: tabdesc.name(),
|
|
thread: thread.name(),
|
|
streams: RefCell::new(HashMap::new()),
|
|
browsing_context_id: id,
|
|
active_pipeline: Cell::new(pipeline),
|
|
};
|
|
|
|
actors.register(Box::new(emulation));
|
|
actors.register(Box::new(inspector));
|
|
actors.register(Box::new(timeline));
|
|
actors.register(Box::new(profiler));
|
|
actors.register(Box::new(performance));
|
|
actors.register(Box::new(styleSheets));
|
|
actors.register(Box::new(thread));
|
|
actors.register(Box::new(tabdesc));
|
|
|
|
target
|
|
}
|
|
|
|
pub fn encodable(&self) -> BrowsingContextActorMsg {
|
|
BrowsingContextActorMsg {
|
|
actor: self.name(),
|
|
traits: BrowsingContextTraits {
|
|
isBrowsingContext: true,
|
|
},
|
|
title: self.title.borrow().clone(),
|
|
url: self.url.borrow().clone(),
|
|
//FIXME: shouldn't ignore pipeline namespace field
|
|
browsingContextId: self.browsing_context_id.index.0.get(),
|
|
//FIXME: shouldn't ignore pipeline namespace field
|
|
outerWindowID: self.active_pipeline.get().index.0.get(),
|
|
consoleActor: self.console.clone(),
|
|
/*emulationActor: self.emulation.clone(),
|
|
inspectorActor: self.inspector.clone(),
|
|
timelineActor: self.timeline.clone(),
|
|
profilerActor: self.profiler.clone(),
|
|
performanceActor: self.performance.clone(),
|
|
styleSheetsActor: self.styleSheets.clone(),*/
|
|
}
|
|
}
|
|
|
|
pub(crate) fn navigate(&self, state: NavigationState) {
|
|
let (pipeline, title, url, state) = match state {
|
|
NavigationState::Start(url) => (None, None, url, "start"),
|
|
NavigationState::Stop(pipeline, info) => {
|
|
(Some(pipeline), Some(info.title), info.url, "stop")
|
|
},
|
|
};
|
|
if let Some(p) = pipeline {
|
|
self.active_pipeline.set(p);
|
|
}
|
|
*self.url.borrow_mut() = url.as_str().to_owned();
|
|
if let Some(ref t) = title {
|
|
*self.title.borrow_mut() = t.clone();
|
|
}
|
|
|
|
let msg = TabNavigated {
|
|
from: self.name(),
|
|
type_: "tabNavigated".to_owned(),
|
|
url: url.as_str().to_owned(),
|
|
title: title,
|
|
nativeConsoleAPI: true,
|
|
state: state.to_owned(),
|
|
isFrameSwitching: false,
|
|
};
|
|
for stream in self.streams.borrow_mut().values_mut() {
|
|
let _ = stream.write_json_packet(&msg);
|
|
}
|
|
}
|
|
|
|
pub(crate) fn title_changed(&self, pipeline: PipelineId, title: String) {
|
|
if pipeline != self.active_pipeline.get() {
|
|
return;
|
|
}
|
|
*self.title.borrow_mut() = title;
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
struct TabNavigated {
|
|
from: String,
|
|
#[serde(rename = "type")]
|
|
type_: String,
|
|
url: String,
|
|
title: Option<String>,
|
|
nativeConsoleAPI: bool,
|
|
state: String,
|
|
isFrameSwitching: bool,
|
|
}
|