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

Web driver bidi #9

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ <h1>Top Page</h1>
<li><a href="./scroll-driven/">Scroll driven animations</a></li>
<li><a href="./scheduler-yield/">scheduler.yield</a></li>
<li><a href="./selectlist/">selectlist</a></li>
<li><a href="./web-driver-bidi/">WebDriver BiDi</a></li>
</ul>
</main>
<script type="module" src="./main.ts"></script>
Expand Down
36 changes: 36 additions & 0 deletions src/web-driver-bidi/event-emitter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
export class EventEmitter {
#listeners;

constructor() {
this.#listeners = new Map();
}

on(eventName, listener) {
if (!this.#listeners.has(eventName)) {
this.#listeners.set(eventName, new Set());
}
this.#listeners.get(eventName).add(listener);
}

off(eventName, listener) {
if (!this.#listeners.has(eventName)) {
return;
}
this.#listeners.get(eventName).delete(listener);
}

emit(eventName, data) {
const listeners = this.#listeners.get(eventName);
if (!listeners) {
return;
}

for (const listener of listeners) {
try {
listener(eventName, data);
} catch (error) {
console.error(`Error in event listener: ${error}`);
}
}
}
}
73 changes: 73 additions & 0 deletions src/web-driver-bidi/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>WebDriver BiDi Client | nus3 UI Labs</title>
</head>
<body>
<header>
<h1>WebDriver BiDi Client</h1>
</header>
<main>
<section>
<h2 style="margin-bottom: 0">How to start</h2>
<div class="flow-section">
<ol>
<li>
Start Firefox with --remote-debugging-port
--remote-allow-origins=https://nus3.github.io
</li>
<li>
Input the URL of the Firefox remote debugging websocket
server(Default: localhost:9222)
</li>
<li>Click connect button</li>
</ol>
<ul>
<li>
<a
href="https://wiki.mozilla.org/Firefox/CommandLineOptions#Using_command_line_options"
target="_blank"
rel="noopener noreferrer"
>
How to start Firefox in cli
</a>
</li>
<li>
<a
href="https://firefox-source-docs.mozilla.org/remote/cdp/Usage.html"
target="_blank"
rel="noopener noreferrer"
>
Usage --remote-debugging-port
</a>
</li>
</ul>
</div>
</section>
<section id="form" class="form">
<h2 style="margin: 0">Operate</h2>
<label>
URL
<input
name="url"
type="text"
autocomplete="off"
value="localhost:9222/session"
/>
<button id="connect" type="button">connect</button>
</label>
<button id="navigate" type="button">navigate</button>
</section>
</main>
<section class="result-wrapper">
<h2 style="margin: 0">Traffic</h2>
<div id="result" class="result"></div>
</section>
<footer>
<a href="../">TOP</a>
</footer>
<script type="module" src="./main.js"></script>
</body>
</html>
101 changes: 101 additions & 0 deletions src/web-driver-bidi/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import './style.css'
import { EventEmitter } from "./event-emitter";

let socket = null
let requestId = 1
// TODO: なんでeventEmitterを使ったら動くのか調べるところから
const eventEmitter = new EventEmitter()

function addLog(log) {
const p = document.createElement('p')
p.textContent = log
document.querySelector('#result').appendChild(p)
}

function reset() {
if (socket) {
socket.close()
socket = null
}
document.querySelector('#result').textContent = ''
}

function formatMessage(msg) {
const obj = JSON.parse(msg)
return JSON.stringify(obj, null, 2)
}

function onMessage (e) {
console.log('Message received from server:', e)
eventEmitter.emit('websocket-message', JSON.parse(e.data))
// addLog(`Message received from server: ${formatMessage(e.data)}`)
}

function connect() {
reset()

const input = document.querySelector('input[name="url"]')
let url = ''
if (input.value.includes('/session')) {
url = `ws://${input.value}`
} else {
url = `ws://${input.value}/session`
}
socket = new WebSocket(url)

socket.onopen = (e) => {
console.log('WebSocket connection opened')
// addLog('WebSocket connection opened')
}
socket.onmessage = onMessage
socket.onclose = (e) => {
console.log('WebSocket connection closed')
// addLog('WebSocket connection closed')
}
}

const btn = document.querySelector('#connect')
btn.addEventListener('click', connect)

function sendMessage(msg) {
if (!socket) return null

const id = requestId++
msg.id = id
console.log(`Message sent to server:`, msg)
// addLog(`Message sent to server: ${formatMessage(msg)}`)
socket.send(JSON.stringify(msg))
return id
}

// リモートエンドにコマンドを送り、帰ってきたIDをみて、同一のIDの場合にresolveする
function sendCommand(method, params) {
const id = sendMessage({ method, params })

return new Promise((resolve) => {
const listener = (eventName, data) => {
if (data.id === id) {
eventEmitter.off('websocket-message', listener)
// socket.removeEventListener('message', listener)
resolve(data)
}
}
eventEmitter.on('websocket-message', listener)
// socket.addEventListener('message', listener)
})
}

const navigateBtn = document.querySelector('#navigate')
navigateBtn.addEventListener('click', async () => {
await sendCommand("session.new", { capabilities: {} });

const res = await sendCommand("browsingContext.getTree", {}, {});
console.log('browsingContext.getTree', res);

const context = res.result.contexts[0].context;
await sendCommand("browsingContext.navigate", {
context,
url: "https://example.com",
wait: "complete"
});
})
114 changes: 114 additions & 0 deletions src/web-driver-bidi/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
:root {
--primary-color: hsl(160 55% 55%);
--primary-color-opacity: hsl(160 55% 55% / 0.6);
--primary-color-opacity-low: hsl(160 55% 55% / 0.2);
--primary-color-hover-opacity: hsl(160 55% 55% / 0.8);
--secondary-color: hsl(42 100% 70%);
--secondary-color-opacity: hsl(42 100% 70% / 0.2);
--secondary-color-hover-opacity: hsl(42 100% 70% / 0.8);
--secondary-color-opacity-low: hsl(42 100% 70% / 0.2);
--bg-color: hsl(216 18% 16%);
--bg-color-opacity: hsl(216 18% 16% / 0.4);
--bg-light-color: hsl(216 10% 24%);
--button-shadow: 0px 6px 11px 1px rgba(31, 37, 45, 0.6);
--text-color: hsl(0 0% 93%);
}

body {
margin: 0;
background-color: var(--bg-color);
margin: 0;
color: var(--text-color);
padding: 2rem;
}

h1 {
color: var(--primary-color);
}

ul > li {
margin-bottom: 1rem;
}

ul > li:last-child {
margin-bottom: 0;
}

ol > li {
margin-bottom: 1rem;
}

ol > li:last-child {
margin-bottom: 0;
}

button {
border-radius: 0.25rem;
border-width: 1px;
border-style: solid;
border-color: var(--primary-color);
background-color: var(--primary-color-opacity);
cursor: pointer;
color: inherit;
padding: 0.6rem;
font-size: 1.2rem;
}

button:hover {
background-color: var(--primary-color-hover-opacity);
}

footer {
position: fixed;
inset-block-end: 0;
padding: 2rem;
padding-left: 0;
}

a {
color: var(--secondary-color);
}

label {
display: flex;
gap: 0.8rem;
align-items: center;
}

input {
height: 28px;
background-color: var(--secondary-color-opacity-low);
border-radius: 0.25rem;
border-width: 1px;
border-style: solid;
border-color: var(--secondary-color);
color: var(--text-color);
padding: 0.4rem;
font-size: 1.2rem;
}

.flow-section {
display: flex;
gap: 0.6rem;
border-bottom: 2px solid var(--bg-light-color);
margin-bottom: 2rem;
}

.form {
display: flex;
flex-direction: column;
gap: 0.8rem;
border-bottom: 2px solid var(--bg-light-color);
margin-bottom: 2rem;
padding-bottom: 2rem;
}

.result-wrapper {
display: flex;
flex-direction: column;
gap: 0.4rem;
}

.result > p {
margin: 0;
}
1 change: 1 addition & 0 deletions vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export default defineConfig({
scrollDriven: resolve(root, 'scroll-driven', 'index.html'),
schedulerYield: resolve(root, 'scheduler-yield', 'index.html'),
selectlist: resolve(root, 'selectlist', 'index.html'),
webDriverBidi: resolve(root, 'web-driver-bidi', 'index.html'),
},
},
},
Expand Down