Browse Source

chore: support --remote-debugging-pipe (#23433)

Pavel Feldman 5 years ago
parent
commit
93f6129c80

+ 7 - 2
shell/browser/electron_browser_main_parts.cc

@@ -439,10 +439,14 @@ void ElectronBrowserMainParts::PreMainMessageLoopRun() {
   content::WebUIControllerFactory::RegisterFactory(
       ElectronWebUIControllerFactory::GetInstance());
 
-  // --remote-debugging-port
   auto* command_line = base::CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(switches::kRemoteDebuggingPort))
+  if (command_line->HasSwitch(switches::kRemoteDebuggingPipe)) {
+    // --remote-debugging-pipe
+    content::DevToolsAgentHost::StartRemoteDebuggingPipeHandler();
+  } else if (command_line->HasSwitch(switches::kRemoteDebuggingPort)) {
+    // --remote-debugging-port
     DevToolsManagerDelegate::StartHttpHandler();
+  }
 
 #if !defined(OS_MACOSX)
   // The corresponding call in macOS is in ElectronApplicationDelegate.
@@ -511,6 +515,7 @@ void ElectronBrowserMainParts::PostMainMessageLoopRun() {
   node_env_.reset();
 
   fake_browser_process_->PostMainMessageLoopRun();
+  content::DevToolsAgentHost::StopRemoteDebuggingPipeHandler();
 }
 
 #if !defined(OS_MACOSX)

+ 35 - 0
spec-main/chromium-spec.ts

@@ -12,6 +12,7 @@ import { EventEmitter } from 'events';
 import { promisify } from 'util';
 import { ifit, ifdescribe } from './spec-helpers';
 import { AddressInfo } from 'net';
+import { PipeTransport } from './pipe-transport';
 
 const features = process.electronBinding('features');
 
@@ -267,6 +268,40 @@ describe('command line switches', () => {
     it('should not set an invalid locale', (done) => testLocale('asdfkl', currentLocale, done));
   });
 
+  describe('--remote-debugging-pipe switch', () => {
+    it('should expose CDP via pipe', async () => {
+      const electronPath = process.execPath;
+      const appProcess = ChildProcess.spawn(electronPath, ['--remote-debugging-pipe'], {
+        stdio: ['pipe', 'pipe', 'pipe', 'pipe', 'pipe']
+      });
+      const stdio = appProcess.stdio as unknown as [NodeJS.ReadableStream, NodeJS.WritableStream, NodeJS.WritableStream, NodeJS.WritableStream, NodeJS.ReadableStream];
+      const pipe = new PipeTransport(stdio[3], stdio[4]);
+      const versionPromise = new Promise(resolve => { pipe.onmessage = resolve; });
+      pipe.send({ id: 1, method: 'Browser.getVersion', params: {} });
+      const message = (await versionPromise) as any;
+      expect(message.id).to.equal(1);
+      expect(message.result.product).to.contain('Chrome');
+      expect(message.result.userAgent).to.contain('Electron');
+      appProcess.kill();
+    });
+    it('should override --remote-debugging-port switch', async () => {
+      const electronPath = process.execPath;
+      const appProcess = ChildProcess.spawn(electronPath, ['--remote-debugging-pipe', '--remote-debugging-port=0'], {
+        stdio: ['pipe', 'pipe', 'pipe', 'pipe', 'pipe']
+      });
+      let stderr = '';
+      appProcess.stderr.on('data', (data) => { stderr += data; });
+      const stdio = appProcess.stdio as unknown as [NodeJS.ReadableStream, NodeJS.WritableStream, NodeJS.WritableStream, NodeJS.WritableStream, NodeJS.ReadableStream];
+      const pipe = new PipeTransport(stdio[3], stdio[4]);
+      const versionPromise = new Promise(resolve => { pipe.onmessage = resolve; });
+      pipe.send({ id: 1, method: 'Browser.getVersion', params: {} });
+      const message = (await versionPromise) as any;
+      expect(message.id).to.equal(1);
+      expect(stderr).to.not.include('DevTools listening on');
+      appProcess.kill();
+    });
+  });
+
   describe('--remote-debugging-port switch', () => {
     it('should display the discovery page', (done) => {
       const electronPath = process.execPath;

+ 37 - 0
spec-main/pipe-transport.ts

@@ -0,0 +1,37 @@
+// A small pipe transport for talking to Electron over CDP.
+export class PipeTransport {
+  private _pipeWrite: NodeJS.WritableStream | null;
+  private _pendingMessage = '';
+
+  onmessage?: (message: string) => void;
+
+  constructor (pipeWrite: NodeJS.WritableStream, pipeRead: NodeJS.ReadableStream) {
+    this._pipeWrite = pipeWrite;
+    pipeRead.on('data', buffer => this._dispatch(buffer));
+  }
+
+  send (message: Object) {
+    this._pipeWrite!.write(JSON.stringify(message));
+    this._pipeWrite!.write('\0');
+  }
+
+  _dispatch (buffer: Buffer) {
+    let end = buffer.indexOf('\0');
+    if (end === -1) {
+      this._pendingMessage += buffer.toString();
+      return;
+    }
+    const message = this._pendingMessage + buffer.toString(undefined, 0, end);
+    if (this.onmessage) { this.onmessage.call(null, JSON.parse(message)); }
+
+    let start = end + 1;
+    end = buffer.indexOf('\0', start);
+    while (end !== -1) {
+      const message = buffer.toString(undefined, start, end);
+      if (this.onmessage) { this.onmessage.call(null, JSON.parse(message)); }
+      start = end + 1;
+      end = buffer.indexOf('\0', start);
+    }
+    this._pendingMessage = buffer.toString(undefined, start);
+  }
+}