Skip to content
This repository has been archived by the owner on Dec 13, 2018. It is now read-only.

Commit

Permalink
iwdp debugger #1 -- introduce end to end iwdp debugger
Browse files Browse the repository at this point in the history
Reviewed By: jgebhardt

Differential Revision: D3963634

fbshipit-source-id: 7cbfff8e4f24ec39270cef080c458cc125517372
  • Loading branch information
johnislarry authored and Facebook Github Bot committed Oct 7, 2016
1 parent 205960a commit 4de9360
Show file tree
Hide file tree
Showing 13 changed files with 517 additions and 0 deletions.
114 changes: 114 additions & 0 deletions pkg/nuclide-debugger-iwdp-rpc/lib/IwdpDebuggerService.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
'use babel';
/* @flow */

/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the license found in the LICENSE file in
* the root directory of this source tree.
*/

import {CompositeDisposable} from 'event-kit';
import {logger} from './logger';
import WS from 'ws';
import xfetch from '../../commons-node/xfetch';
import fsPromise from '../../commons-node/fsPromise';

import type {ConnectableObservable} from 'rxjs';

const {log} = logger;

let lastServiceObjectDispose = null;

import {ClientCallback} from '../../nuclide-debugger-common/lib/main';

export class IwdpDebuggerService {
_state: string;
_clientCallback: ClientCallback;
_disposables: CompositeDisposable;
_webSocket: ?WS;
_package: ?string;

constructor() {
if (lastServiceObjectDispose != null) {
lastServiceObjectDispose();
}
lastServiceObjectDispose = this.dispose.bind(this);
this._disposables = new CompositeDisposable();
this._clientCallback = new ClientCallback();
this._disposables.add(this._clientCallback);
}

getServerMessageObservable(): ConnectableObservable<string> {
return this._clientCallback.getServerMessageObservable().publish();
}

async debug(): Promise<string> {
// TODO
// 1. start ios-webkit-debug-proxy
// 2. see what devices there are
// 3. send request to json endpoint to get web socket data
// 4. set up websocket to iwdp server.

const webSocket = new WS('ws://localhost:9222/devtools/page/1');
this._webSocket = webSocket;
webSocket.on(
'message',
async message => {
const obj = JSON.parse(message);
if (obj.method !== 'Debugger.scriptParsed') {
this._clientCallback.sendChromeMessage(message);
} else if (
obj.params == null || obj.params.url == null || !obj.params.url.startsWith('http:')
) {
this._clientCallback.sendChromeMessage(message);
} else {
// It's a bundle being served by the webserver hosted by the target, so we should
// download it.
const {url} = obj.params;
this._package = url;
const response = await xfetch(url, {});
const text = await response.text();
const tempPath = await fsPromise.tempfile({prefix: 'jsfile', suffix: '.js'});
await fsPromise.writeFile(tempPath, text);
obj.params.url = `file://${tempPath}`;


// Also source maps
const SOURCE_MAP_REGEX = /\/\/# sourceMappingURL=(.+)$/;
const matches = SOURCE_MAP_REGEX.exec(text);
const sourceMapUrl = `http://localhost:8081${matches[1]}`;

const response2 = await xfetch(sourceMapUrl, {});
const text2 = await response2.text();
const base64Text = new Buffer(text2).toString('base64');
obj.params.sourceMapURL = `data:application/json;base64,${base64Text}`;

this._clientCallback.sendChromeMessage(JSON.stringify(obj));
}
},
);

log('IWDP Connected');
return 'IWDP connected';
}

async sendCommand(message: string): Promise<void> {
if (this._webSocket == null) {
return;
}
const webSocket = this._webSocket;
const obj = JSON.parse(message);
if (obj.method === 'Debugger.setBreakpointByUrl') {
obj.params.url = this._package;
webSocket.send(JSON.stringify(obj));
} else {
webSocket.send(message);
}
}

async dispose(): Promise<void> {
this._disposables.dispose();
}
}
16 changes: 16 additions & 0 deletions pkg/nuclide-debugger-iwdp-rpc/lib/logger.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
'use babel';
/* @flow */

/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the license found in the LICENSE file in
* the root directory of this source tree.
*/

import {getCategoryLogger} from '../../nuclide-logging';

const DEBUGGER_LOGGER_CATEGORY = 'nuclide-debugger-iwdp-rpc';

export const logger = getCategoryLogger(DEBUGGER_LOGGER_CATEGORY);
14 changes: 14 additions & 0 deletions pkg/nuclide-debugger-iwdp-rpc/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "nuclide-debugger-iwdp-rpc",
"repository": "https://github.com/facebook/nuclide",
"main": "./lib/IwdpDebuggerService.js",
"version": "0.0.0",
"description": "Adapter between Nuclide's debugger client and the iOS webkit debug proxy",
"nuclide": {
"packageType": "Node",
"testRunner": "npm"
},
"scripts": {
"test": "node ../nuclide-jasmine/bin/jasmine-node-transpiled spec"
}
}
1 change: 1 addition & 0 deletions pkg/nuclide-debugger-iwdp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# IOS Webkit Proxy Debugger
25 changes: 25 additions & 0 deletions pkg/nuclide-debugger-iwdp/lib/AttachProcessInfo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
'use babel';
/* @flow */

/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the license found in the LICENSE file in
* the root directory of this source tree.
*/

import {DebuggerProcessInfo} from '../../nuclide-debugger-base';
import {IwdpDebuggerInstance} from './IwdpDebuggerInstance';

import type {NuclideUri} from '../../commons-node/nuclideUri';

export class AttachProcessInfo extends DebuggerProcessInfo {
constructor(targetUri: NuclideUri) {
super('iwdp', targetUri);
}

async debug(): Promise<IwdpDebuggerInstance> {
return new IwdpDebuggerInstance(this);
}
}
100 changes: 100 additions & 0 deletions pkg/nuclide-debugger-iwdp/lib/AttachUiComponent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
'use babel';
/* @flow */

/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the license found in the LICENSE file in
* the root directory of this source tree.
*/

import {React} from 'react-for-atom';
import {AttachProcessInfo} from './AttachProcessInfo';
import {Button, ButtonTypes} from '../../nuclide-ui/Button';
import {Dropdown} from '../../nuclide-ui/Dropdown';
import consumeFirstProvider from '../../commons-atom/consumeFirstProvider';

import type {NuclideUri} from '../../commons-node/nuclideUri';

type PropsType = {
targetUri: NuclideUri,
};

type StateType = {
selectedPathIndex: number,
pathMenuItems: Array<{label: string, value: number}>,
};

export class AttachUiComponent extends React.Component<void, PropsType, StateType> {
props: PropsType;
state: StateType;

constructor(props: PropsType) {
super(props);
(this: any)._handleCancelButtonClick = this._handleCancelButtonClick.bind(this);
(this: any)._handleAttachButtonClick = this._handleAttachButtonClick.bind(this);
(this: any)._handlePathsDropdownChange = this._handlePathsDropdownChange.bind(this);
this.state = {
selectedPathIndex: 0,
pathMenuItems: this._getPathMenuItems(),
};
}

render(): React.Element<any> {
return (
<div className="block">
<div className="nuclide-debugger-iwdp-launch-attach-ui-select-project">
<label>Selected Project Directory: </label>
<Dropdown
className="inline-block nuclide-debugger-connection-box"
options={this.state.pathMenuItems}
onChange={this._handlePathsDropdownChange}
value={this.state.selectedPathIndex}
/>
</div>
<div className="padded text-right">
<Button onClick={this._handleCancelButtonClick}>Cancel</Button>
<Button
buttonType={ButtonTypes.PRIMARY}
onClick={this._handleAttachButtonClick}>
Attach
</Button>
</div>
</div>
);
}

_getPathMenuItems(): Array<{label: string, value: number}> {
return [];
}

_handlePathsDropdownChange(newIndex: number): void {
this.setState({
selectedPathIndex: newIndex,
pathMenuItems: this._getPathMenuItems(),
});
}

_handleAttachButtonClick(): void {
const processInfo = new AttachProcessInfo(this.props.targetUri);
consumeFirstProvider('nuclide-debugger.remote')
.then(debuggerService => debuggerService.startDebugging(processInfo));
this._showDebuggerPanel();
this._handleCancelButtonClick();
}

_showDebuggerPanel(): void {
atom.commands.dispatch(
atom.views.getView(atom.workspace),
'nuclide-debugger:show',
);
}

_handleCancelButtonClick(): void {
atom.commands.dispatch(
atom.views.getView(atom.workspace),
'nuclide-debugger:toggle-launch-attach',
);
}
}
Loading

0 comments on commit 4de9360

Please sign in to comment.