Rewild Your Web
18
fork

Configure Feed

Select the types of activity you want to include in your feed.

p2p: unpairing support

webbeef 62b5141e 6b76e3f5

+157 -25
+10
crates/beaver_p2p/src/lib.rs
··· 404 404 crate::state::EndpointProxy::new(name, *id, addr, EndpointStatus::PairedDisconnected); 405 405 inner.state.lock().await.add_endpoint(id, proxy); 406 406 } 407 + 408 + /// Remove a paired peer from the runtime state. 409 + pub async fn remove_paired_peer(&self, id: &EndpointId) { 410 + let Some(ref inner) = self.inner else { 411 + error!("Not initialized"); 412 + return; 413 + }; 414 + 415 + inner.state.lock().await.remove_endpoint(id); 416 + } 407 417 }
+7
crates/beaver_p2p/src/state.rs
··· 183 183 } 184 184 } 185 185 186 + pub(crate) fn remove_endpoint(&mut self, id: &EndpointId) { 187 + self.endpoints.remove(id); 188 + self.pairing_requested.remove(id); 189 + self.pending_ack.remove(id); 190 + self.pairing_responders.remove(id); 191 + } 192 + 186 193 fn discovered(&self, id: &EndpointId) -> bool { 187 194 self.endpoints 188 195 .get(id)
+16 -15
patches/components/constellation/constellation.rs.patch
··· 308 308 ScriptToConstellationMessage::MediaSessionEvent(pipeline_id, event) => { 309 309 // Unlikely at this point, but we may receive events coming from 310 310 // different media sessions, so we set the active media session based 311 - @@ -2057,9 +2185,358 @@ 311 + @@ -2057,7 +2185,359 @@ 312 312 let _ = event_loop.send(ScriptThreadMessage::TriggerGarbageCollection); 313 313 } 314 314 }, ··· 459 459 + ScriptToConstellationMessage::PairingRejectPairing(id, callback) => { 460 460 + self.pairing.reject_pairing(&id, callback); 461 461 + }, 462 + + ScriptToConstellationMessage::PairingRemovePeer(id, callback) => { 463 + + self.pairing.remove_peer(&id, callback); 464 + + }, 462 465 + ScriptToConstellationMessage::CreatePeerStream( 463 466 + peer_id, 464 467 + local_port_id, ··· 522 525 + .send_message(&from_peer, &P2pMessage::PortOfferDenied { stream_id }); 523 526 + } 524 527 + }, 525 - } 526 - } 527 - 528 + + } 529 + + } 530 + + 528 531 + fn handle_pairing_event(&mut self, event: constellation_traits::PairingEvent) { 529 532 + if let constellation_traits::PairingEvent::MessageReceived { ref from, ref data } = event { 530 533 + debug!("P2P message received from {from}, {} bytes", data.len()); ··· 655 658 + // Handle peer disconnect: clean up remote channel state. 656 659 + if let constellation_traits::PairingEvent::PeerExpired { ref id } = event { 657 660 + self.pairing.clear_remote_peer(id); 658 - + } 661 + } 659 662 + 660 663 + for event_loop in self.event_loops() { 661 664 + if self.embedder_error_listeners.contains(&event_loop.id()) { 662 665 + let _ = event_loop.send(ScriptThreadMessage::DispatchPairingEvent(event.clone())); 663 666 + } 664 667 + } 665 - + } 666 - + 668 + } 669 + 667 670 /// Check the origin of a message against that of the pipeline it came from. 668 - /// Note: this is still limited as a security check, 669 - /// see <https://github.com/servo/servo/issues/11722> 670 - @@ -2376,6 +2853,29 @@ 671 + @@ -2376,6 +2856,29 @@ 671 672 TransferState::TransferInProgress(queue) => queue.push_back(task), 672 673 TransferState::CompletionFailed(queue) => queue.push_back(task), 673 674 TransferState::CompletionRequested(_, queue) => queue.push_back(task), ··· 697 698 } 698 699 } 699 700 700 - @@ -3246,6 +3746,13 @@ 701 + @@ -3246,6 +3749,13 @@ 701 702 /// <https://html.spec.whatwg.org/multipage/#destroy-a-top-level-traversable> 702 703 fn handle_close_top_level_browsing_context(&mut self, webview_id: WebViewId) { 703 704 debug!("{webview_id}: Closing"); ··· 711 712 let browsing_context_id = BrowsingContextId::from(webview_id); 712 713 // Step 5. Remove traversable from the user agent's top-level traversable set. 713 714 let browsing_context = 714 - @@ -3522,8 +4029,27 @@ 715 + @@ -3522,8 +4032,27 @@ 715 716 opener_webview_id, 716 717 opener_pipeline_id, 717 718 response_sender, ··· 739 740 let Some((webview_id_sender, webview_id_receiver)) = generic_channel::channel() else { 740 741 warn!("Failed to create channel"); 741 742 let _ = response_sender.send(None); 742 - @@ -3622,6 +4148,361 @@ 743 + @@ -3622,6 +4151,361 @@ 743 744 }); 744 745 } 745 746 ··· 1101 1102 #[servo_tracing::instrument(skip_all)] 1102 1103 fn handle_refresh_cursor(&self, pipeline_id: PipelineId) { 1103 1104 let Some(pipeline) = self.pipelines.get(&pipeline_id) else { 1104 - @@ -4747,7 +5628,7 @@ 1105 + @@ -4747,7 +5631,7 @@ 1105 1106 } 1106 1107 1107 1108 #[servo_tracing::instrument(skip_all)] ··· 1110 1111 // Send a flat projection of the history to embedder. 1111 1112 // The final vector is a concatenation of the URLs of the past 1112 1113 // entries, the current entry and the future entries. 1113 - @@ -4850,9 +5731,23 @@ 1114 + @@ -4850,9 +5734,23 @@ 1114 1115 ); 1115 1116 self.embedder_proxy.send(EmbedderMsg::HistoryChanged( 1116 1117 webview_id,
+35 -1
patches/components/constellation/pairing.rs.patch
··· 1 1 --- original 2 2 +++ modified 3 - @@ -0,0 +1,698 @@ 3 + @@ -0,0 +1,732 @@ 4 4 +// SPDX-License-Identifier: AGPL-3.0-or-later 5 5 + 6 6 +//! P2P pairing service integration with the constellation. ··· 378 378 + let _ = callback.send(Err(format!("Reject pairing failed: {e}"))); 379 379 + }, 380 380 + } 381 + + }); 382 + + } 383 + + 384 + + /// Remove a paired peer: delete from runtime state, persisted config, and remote channels. 385 + + pub(crate) fn remove_peer(&self, id: &str, callback: GenericCallback<Result<(), String>>) { 386 + + let endpoint_id: EndpointId = match id.parse() { 387 + + Ok(id) => id, 388 + + Err(e) => { 389 + + let _ = callback.send(Err(format!("Invalid endpoint ID: {e}"))); 390 + + return; 391 + + }, 392 + + }; 393 + + let id = id.to_owned(); 394 + + let manager = self.manager.clone(); 395 + + let remote_channels = self.remote_channels.clone(); 396 + + net::async_runtime::spawn_task(async move { 397 + + // Remove from beaver_p2p runtime state. 398 + + { 399 + + let guard = manager.lock().await; 400 + + if let Some(mgr) = guard.as_ref() { 401 + + mgr.remove_paired_peer(&endpoint_id).await; 402 + + } 403 + + } 404 + + 405 + + // Remove from persisted config. 406 + + let config_dir = servo_config::opts::get().config_dir.clone(); 407 + + let mut config = load_config(config_dir.as_ref()).await; 408 + + config.paired_peers.retain(|p| p.id != id); 409 + + save_config(config_dir.as_ref(), &config).await; 410 + + 411 + + // Clean up remote channel state. 412 + + remote_channels.lock().await.remove(&id); 413 + + 414 + + let _ = callback.send(Ok(())); 381 415 + }); 382 416 + } 383 417 +
+2 -1
patches/components/constellation/tracing.rs.patch
··· 36 36 Self::ActivateDocument => target!("ActivateDocument"), 37 37 Self::SetDocumentState(..) => target!("SetDocumentState"), 38 38 Self::SetFinalUrl(..) => target!("SetFinalUrl"), 39 - @@ -186,6 +194,45 @@ 39 + @@ -186,6 +194,46 @@ 40 40 target!("RespondToScreenshotReadinessRequest") 41 41 }, 42 42 Self::TriggerGarbageCollection => target!("TriggerGarbageCollection"), ··· 77 77 + Self::PairingRequestPairing(..) => target!("PairingRequestPairing"), 78 78 + Self::PairingAcceptPairing(..) => target!("PairingAcceptPairing"), 79 79 + Self::PairingRejectPairing(..) => target!("PairingRejectPairing"), 80 + + Self::PairingRemovePeer(..) => target!("PairingRemovePeer"), 80 81 + Self::CreatePeerStream(..) => target!("CreatePeerStream"), 81 82 + Self::PeerStreamResponse(..) => target!("PeerStreamResponse"), 82 83 }
+20 -1
patches/components/script/dom/pairing.rs.patch
··· 1 1 --- original 2 2 +++ modified 3 - @@ -0,0 +1,296 @@ 3 + @@ -0,0 +1,315 @@ 4 4 +/* SPDX Id: AGPL-3.0-or-later */ 5 5 + 6 6 +use std::rc::Rc; ··· 148 148 + let chan = global.script_to_constellation_chan(); 149 149 + if chan 150 150 + .send(ScriptToConstellationMessage::PairingRejectPairing( 151 + + peer.id().to_string(), 152 + + callback, 153 + + )) 154 + + .is_err() 155 + + { 156 + + promise.reject_error(Error::Operation(None), can_gc); 157 + + } 158 + + promise 159 + + } 160 + + 161 + + fn RemovePeer(&self, peer: &Peer, comp: InRealm, can_gc: CanGc) -> Rc<Promise> { 162 + + let global = &self.global(); 163 + + let promise = Promise::new_in_current_realm(comp, can_gc); 164 + + let task_source = global.task_manager().dom_manipulation_task_source(); 165 + + let callback = callback_promise(&promise, self, task_source); 166 + + 167 + + let chan = global.script_to_constellation_chan(); 168 + + if chan 169 + + .send(ScriptToConstellationMessage::PairingRemovePeer( 151 170 + peer.id().to_string(), 152 171 + callback, 153 172 + ))
+2 -2
patches/components/script_bindings/codegen/Bindings.conf.patch
··· 28 28 }, 29 29 30 30 +'Pairing': { 31 - + 'inRealms': ['Start', 'Stop', 'Local', 'Peers', 'RequestPairing', 'SetName', 'AcceptPairing', 'RejectPairing'], 32 - + 'canGc': ['Start', 'Stop', 'Local', 'Peers', 'RequestPairing', 'SetName', 'AcceptPairing', 'RejectPairing'], 31 + + 'inRealms': ['Start', 'Stop', 'Local', 'Peers', 'RequestPairing', 'SetName', 'AcceptPairing', 'RejectPairing', 'RemovePeer'], 32 + + 'canGc': ['Start', 'Stop', 'Local', 'Peers', 'RequestPairing', 'SetName', 'AcceptPairing', 'RejectPairing', 'RemovePeer'], 33 33 +}, 34 34 + 35 35 'PerformanceObserver': {
+4 -1
patches/components/script_bindings/webidls/Pairing.webidl.patch
··· 1 1 --- original 2 2 +++ modified 3 - @@ -0,0 +1,68 @@ 3 + @@ -0,0 +1,71 @@ 4 4 +/* SPDX Id: AGPL-3.0-or-later */ 5 5 + 6 6 +[Exposed=Window, ··· 51 51 + 52 52 + // Reject an incoming pairing request. 53 53 + Promise<undefined> rejectPairing(Peer peer); 54 + + 55 + + // Remove a paired peer (unpair and forget). 56 + + Promise<undefined> removePeer(Peer peer); 54 57 + 55 58 + // A new unpaired peer was discovered. 56 59 + attribute EventHandler onpeerdiscovered;
+3 -1
patches/components/shared/constellation/from_script_message.rs.patch
··· 119 119 /// Mark a new document as active 120 120 ActivateDocument, 121 121 /// Set the document state for a pipeline (used by screenshot / reftests) 122 - @@ -726,6 +775,73 @@ 122 + @@ -726,6 +775,75 @@ 123 123 RespondToScreenshotReadinessRequest(ScreenshotReadinessResponse), 124 124 /// Request the constellation to force garbage collection in all `ScriptThread`'s. 125 125 TriggerGarbageCollection, ··· 177 177 + PairingAcceptPairing(String, GenericCallback<Result<(), String>>), 178 178 + /// Reject an incoming pairing request from a remote peer. 179 179 + PairingRejectPairing(String, GenericCallback<Result<(), String>>), 180 + + /// Remove a paired peer (unpair and forget). 181 + + PairingRemovePeer(String, GenericCallback<Result<(), String>>), 180 182 + /// Create a peer stream: create a virtual remote port entangled with a local port, 181 183 + /// and send the offer to a remote peer. 182 184 + /// Args: peer_id, local_port_id, remote_port_id, target_url, callback.
+27
ui/settings/index.css
··· 590 590 background: #5a3a2d; 591 591 color: #df9f8f; 592 592 } 593 + 594 + .p2p-peer-actions { 595 + display: flex; 596 + align-items: center; 597 + gap: var(--spacing-sm); 598 + } 599 + 600 + .p2p-remove-btn { 601 + padding: var(--spacing-xs) var(--spacing-sm); 602 + border: 1px solid var(--color-danger); 603 + border-radius: var(--radius-sm); 604 + background: none; 605 + color: var(--color-danger); 606 + cursor: pointer; 607 + font-size: var(--font-size-sm); 608 + white-space: nowrap; 609 + } 610 + 611 + .p2p-remove-btn:hover { 612 + background: var(--color-danger); 613 + color: white; 614 + } 615 + 616 + .p2p-remove-btn:disabled { 617 + opacity: 0.5; 618 + cursor: not-allowed; 619 + }
+31 -3
ui/settings/index.js
··· 281 281 <div class="p2p-peer-name">${p.displayName || p.id}</div> 282 282 <div class="p2p-peer-id">${p.id}</div> 283 283 </div> 284 - ${p.status === "discovered" 285 - ? `<button class="p2p-pair-btn" data-id="${p.id}">Pair</button>` 286 - : `<span class="p2p-peer-status ${p.status}">${p.status}</span>`} 284 + <div class="p2p-peer-actions"> 285 + ${ 286 + p.status === "discovered" 287 + ? `<button class="p2p-pair-btn" data-id="${p.id}">Pair</button>` 288 + : `<span class="p2p-peer-status ${p.status}">${p.status}</span> 289 + <button class="p2p-remove-btn" data-id="${p.id}">Remove</button>` 290 + } 291 + </div> 287 292 </div>`, 288 293 ) 289 294 .join(""); ··· 306 311 await refreshPeers(); 307 312 } catch (e) { 308 313 console.error("Pairing failed:", e); 314 + btn.textContent = "Failed"; 315 + setTimeout(() => refreshPeers(), 2000); 316 + } 317 + }); 318 + }); 319 + 320 + // Attach remove button handlers 321 + peerList.querySelectorAll(".p2p-remove-btn").forEach((btn) => { 322 + btn.addEventListener("click", async () => { 323 + const id = btn.dataset.id; 324 + btn.disabled = true; 325 + btn.textContent = "Removing..."; 326 + try { 327 + const peersList = await pairing.peers(); 328 + const peer = peersList.find((p) => p.id === id); 329 + if (!peer) { 330 + btn.textContent = "Not found"; 331 + return; 332 + } 333 + await pairing.removePeer(peer); 334 + await refreshPeers(); 335 + } catch (e) { 336 + console.error("Remove peer failed:", e); 309 337 btn.textContent = "Failed"; 310 338 setTimeout(() => refreshPeers(), 2000); 311 339 }