├── .eslintignore ├── karma-tests ├── components │ ├── actors-panel-spec.js │ ├── packets-panel-spec.js │ ├── packets-sidebar-spec.js │ ├── main-tabbed-area-spec.js │ ├── packets-toolbar-spec.js │ └── packet-spec.js ├── .eslintrc ├── custom-react-matchers.js ├── lib │ └── firebug-sdk-shims.js └── test-main.js ├── data ├── inspector │ ├── res │ │ ├── icon-16.png │ │ ├── sent-arrow.png │ │ ├── received-arrow.png │ │ ├── sent-arrow-selected.png │ │ ├── received-arrow-selected.png │ │ └── arrow.svg │ ├── actions │ │ ├── index.js │ │ ├── global.js │ │ ├── actors.js │ │ └── packets.js │ ├── css │ │ ├── base.css │ │ ├── actors-panel.css │ │ ├── search-box.css │ │ ├── toolbar.css │ │ ├── tree-editor-view.css │ │ ├── toolbox.css │ │ └── packets-panel.css │ ├── store.js │ ├── reducers │ │ ├── index.js │ │ ├── global.js │ │ ├── actors.js │ │ └── packets.js │ ├── search.js │ ├── config.js │ ├── containers │ │ └── app.js │ ├── components │ │ ├── text-with-tooltip.js │ │ ├── packets-message.js │ │ ├── packet-details.js │ │ ├── packets-limit.js │ │ ├── actors-toolbar.js │ │ ├── search-box.js │ │ ├── packet-stack-sidepanel.js │ │ ├── packets-panel.js │ │ ├── packets-sidebar.js │ │ ├── packets-summary.js │ │ ├── packet-list.js │ │ ├── main-tabbed-area.js │ │ ├── factories.js │ │ ├── pools.js │ │ ├── packets-toolbar.js │ │ ├── packet-editor.js │ │ ├── stack-frame-rep.js │ │ ├── packet.js │ │ └── actors-panel.js │ ├── main.html │ ├── main.js │ └── frame-script.js ├── lib │ ├── bootstrap │ │ └── fonts │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ ├── glyphicons-halflings-regular.woff │ │ │ └── glyphicons-halflings-regular.woff2 │ └── react │ │ └── react-dom.js ├── shared │ ├── react-bootstrap-factories.js │ ├── react-helpers.js │ ├── resizer.js │ └── rdp-inspector-window.js ├── connection-list │ ├── css │ │ └── connection-list.css │ ├── actions.js │ ├── store.js │ ├── reducers.js │ ├── config.js │ ├── containers │ │ └── main-panel.js │ ├── index.html │ ├── index.js │ └── components │ │ └── connection-list.js └── .eslintrc ├── .jshintrc ├── chrome ├── skin │ └── classic │ │ └── shared │ │ ├── icon16.png │ │ ├── icon32.png │ │ ├── icon64.png │ │ └── search.svg ├── icons │ └── default │ │ └── rdpinspectorconsole.ico ├── locale │ ├── en-US │ │ ├── connections.properties │ │ ├── toolbox.properties │ │ └── inspector.properties │ ├── zh-CN │ │ ├── toolbox.properties │ │ └── inspector.properties │ ├── nl │ │ ├── toolbox.properties │ │ └── inspector.properties │ └── pt-BR │ │ ├── toolbox.properties │ │ └── inspector.properties └── content │ ├── connections-window.xul │ └── inspector-window.xul ├── test ├── .eslintrc └── test-inspector-service.js ├── lib ├── .eslintrc ├── inspector-front.js ├── main.js ├── webide-connections-monitor.js ├── transport-observer.js ├── toolbox-overlay.js ├── connection-list-window.js ├── inspector-actor.js └── start-button.js ├── .jpmignore ├── .eslintrc ├── .gitignore ├── chrome.manifest ├── README.md ├── .travis.yml ├── index.js ├── karma.conf.js └── package.json /.eslintignore: -------------------------------------------------------------------------------- 1 | coverage/ 2 | data/lib/ 3 | karma-tests/lib/ 4 | -------------------------------------------------------------------------------- /karma-tests/components/actors-panel-spec.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | -------------------------------------------------------------------------------- /data/inspector/res/icon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebug/rdp-inspector/HEAD/data/inspector/res/icon-16.png -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "esnext": "true", 3 | "predef": [ "require", "exports", "module" ], 4 | "curly": "true" 5 | } 6 | -------------------------------------------------------------------------------- /data/inspector/res/sent-arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebug/rdp-inspector/HEAD/data/inspector/res/sent-arrow.png -------------------------------------------------------------------------------- /chrome/skin/classic/shared/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebug/rdp-inspector/HEAD/chrome/skin/classic/shared/icon16.png -------------------------------------------------------------------------------- /chrome/skin/classic/shared/icon32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebug/rdp-inspector/HEAD/chrome/skin/classic/shared/icon32.png -------------------------------------------------------------------------------- /chrome/skin/classic/shared/icon64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebug/rdp-inspector/HEAD/chrome/skin/classic/shared/icon64.png -------------------------------------------------------------------------------- /data/inspector/res/received-arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebug/rdp-inspector/HEAD/data/inspector/res/received-arrow.png -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | // extends common eslintrc config with addon-sdk specific configs 2 | { 3 | "extends": "../lib/.eslintrc" 4 | } 5 | -------------------------------------------------------------------------------- /data/inspector/res/sent-arrow-selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebug/rdp-inspector/HEAD/data/inspector/res/sent-arrow-selected.png -------------------------------------------------------------------------------- /chrome/icons/default/rdpinspectorconsole.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebug/rdp-inspector/HEAD/chrome/icons/default/rdpinspectorconsole.ico -------------------------------------------------------------------------------- /data/inspector/res/received-arrow-selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebug/rdp-inspector/HEAD/data/inspector/res/received-arrow-selected.png -------------------------------------------------------------------------------- /data/lib/bootstrap/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebug/rdp-inspector/HEAD/data/lib/bootstrap/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /data/lib/bootstrap/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebug/rdp-inspector/HEAD/data/lib/bootstrap/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /data/lib/bootstrap/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebug/rdp-inspector/HEAD/data/lib/bootstrap/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /data/lib/bootstrap/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firebug/rdp-inspector/HEAD/data/lib/bootstrap/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /karma-tests/.eslintrc: -------------------------------------------------------------------------------- 1 | // extends data eslintrc config with karma tests specific configs 2 | { 3 | "extends": "../data/.eslintrc", 4 | "env": {}, 5 | "rules": {}, 6 | "globals": {} 7 | } 8 | -------------------------------------------------------------------------------- /chrome/locale/en-US/connections.properties: -------------------------------------------------------------------------------- 1 | # LOCALIZATION NOTE (rdpConnections.tab.LocalTabs, rdpConnections.tab.WebIDE) 2 | # A label for "Connect To..." window tabs. 3 | rdpConnections.tab.LocalTabs=Local Tabs 4 | rdpConnections.tab.WebIDE=WebIDE 5 | -------------------------------------------------------------------------------- /data/shared/react-bootstrap-factories.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | define(function(require, exports, module) { 4 | 5 | "use strict"; 6 | 7 | const ReactBootstrap = require("react-bootstrap"); 8 | const { ReactLazyFactories } = require("shared/react-helpers"); 9 | 10 | module.exports = ReactLazyFactories(ReactBootstrap); 11 | 12 | }); 13 | -------------------------------------------------------------------------------- /data/connection-list/css/connection-list.css: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | /******************************************************************************/ 4 | /* Connection List Window */ 5 | 6 | .tab-content .list-group-item { 7 | border-radius: 0; 8 | } 9 | 10 | .tab-content .list-group-item:first-child { 11 | border-top: 0; 12 | } 13 | -------------------------------------------------------------------------------- /data/inspector/actions/index.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | define(function(require, exports, module) { 4 | 5 | "use strict"; 6 | 7 | const global = require("./global"); 8 | const actors = require("./actors"); 9 | const packets = require("./packets"); 10 | 11 | module.exports = Object.assign({}, global, actors, packets, { 12 | types: Object.assign({}, actors.types, packets.types) 13 | }); 14 | 15 | }); 16 | -------------------------------------------------------------------------------- /data/connection-list/actions.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | define(function(require, exports, module) { 4 | 5 | "use strict"; 6 | 7 | const types = { 8 | SET_CONNECTIONS: "SET_CONNECTIONS" 9 | }; 10 | 11 | // export actions types and action creators 12 | module.exports = { 13 | types, 14 | 15 | setConnections(connections) { 16 | return { type: types.SET_CONNECTIONS, connections }; 17 | } 18 | }; 19 | 20 | }); 21 | -------------------------------------------------------------------------------- /data/inspector/actions/global.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | define(function(require, exports, module) { 4 | 5 | "use strict"; 6 | 7 | const types = { 8 | SET_SEARCH_FILTER: "SET_SEARCH_FILTER", 9 | }; 10 | 11 | // export actions types and action creators 12 | module.exports = { 13 | types, 14 | 15 | setSearchFilter(filter) { 16 | return { type: types.SET_SEARCH_FILTER, filter }; 17 | }, 18 | }; 19 | 20 | }); 21 | -------------------------------------------------------------------------------- /lib/.eslintrc: -------------------------------------------------------------------------------- 1 | // extends common eslintrc config with addon-sdk specific configs 2 | { 3 | "extends": "../.eslintrc", 4 | "env": { 5 | // commonjs conventions 6 | "node": true 7 | }, 8 | "rules": { 9 | "global-strict": 0, 10 | "dot-notation": 0, 11 | "new-cap": 0, 12 | "no-underscore-dangle": 0 13 | }, 14 | "globals": { 15 | // firebug.sdk globals 16 | "FBTrace": true 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.jpmignore: -------------------------------------------------------------------------------- 1 | # Doc Files 2 | /docs/ 3 | README.md 4 | 5 | # Test Files 6 | /test/ 7 | 8 | karma.conf.js 9 | /karma-tests/ 10 | 11 | # GIT 12 | /.git/ 13 | 14 | # exclude hidden files 15 | .* 16 | 17 | # Existing packages 18 | *.xpi 19 | 20 | # npm debug log file 21 | npm-debug.log 22 | 23 | # Excludes npm devDependecies 24 | /node_modules/ 25 | 26 | # Includes firebug.sdk 27 | !/node_modules/firebug.sdk 28 | 29 | # Code Coverage logs & reports 30 | /coverage/ 31 | -------------------------------------------------------------------------------- /data/inspector/css/base.css: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | /******************************************************************************/ 4 | /* General */ 5 | 6 | body { 7 | background-color: white; 8 | padding: 0; 9 | margin: 0; 10 | height: 100%; 11 | } 12 | 13 | *:focus { 14 | outline: none !important; 15 | } 16 | 17 | #content { 18 | height: 100%; 19 | } 20 | 21 | #content > DIV { 22 | height: 100%; 23 | } 24 | -------------------------------------------------------------------------------- /data/connection-list/store.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | define(function(require, exports/*, module*/) { 4 | 5 | "use strict"; 6 | 7 | // Redux 8 | const { createStore } = require("redux"); 9 | 10 | // RDP Inspector 11 | const { rootReducer } = require("./reducers"); 12 | 13 | function configureStore(initialState) { 14 | return createStore(rootReducer, initialState); 15 | } 16 | 17 | // Exports from this module 18 | exports.configureStore = configureStore; 19 | 20 | }); 21 | -------------------------------------------------------------------------------- /data/inspector/store.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | define(function(require, exports/*, module*/) { 4 | 5 | "use strict"; 6 | 7 | // Redux 8 | const { createStore } = require("redux"); 9 | 10 | // RDP Inspector 11 | const { rootReducer } = require("./reducers/index"); 12 | 13 | function configureStore(initialState) { 14 | return createStore(rootReducer, initialState); 15 | } 16 | 17 | // Exports from this module 18 | exports.configureStore = configureStore; 19 | 20 | }); 21 | -------------------------------------------------------------------------------- /data/inspector/reducers/index.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | define(function(require, exports/*, module*/) { 4 | 5 | "use strict"; 6 | 7 | // Redux 8 | const { combineReducers } = require("redux"); 9 | 10 | // RDP Inspector 11 | const global = require("./global"); 12 | const actors = require("./actors"); 13 | const packets = require("./packets"); 14 | 15 | var rootReducer = combineReducers({ global, actors, packets }); 16 | 17 | // Exports from this module 18 | exports.rootReducer = rootReducer; 19 | }); 20 | -------------------------------------------------------------------------------- /data/inspector/actions/actors.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | define(function(require, exports, module) { 4 | 5 | "use strict"; 6 | 7 | const types = { 8 | SET_ACTORS: "SET_ACTORS", 9 | CLEAR_ACTORS: "CLEAR_ACTORS" 10 | }; 11 | 12 | // export actions types and action creators 13 | module.exports = { 14 | types, 15 | 16 | setActors(actors) { 17 | return { type: types.SET_ACTORS, actors }; 18 | }, 19 | 20 | clearActors() { 21 | return { type: types.CLEAR_ACTORS }; 22 | }, 23 | }; 24 | 25 | }); 26 | -------------------------------------------------------------------------------- /chrome/locale/zh-CN/toolbox.properties: -------------------------------------------------------------------------------- 1 | rdpInspector.title=RDP Inspector 2 | rdpInspector.startButton.title=RDP Inspector 3 | rdpInspector.startButton.tip=远程调试协议探查器。 4 | rdpInspector.menu.about.label=关于 RDP Inspector... 5 | rdpInspector.menu.about.tip=有关 RDP Inspector 的信息 6 | rdpInspector.menu.visitHomePage.label=访问主页... 7 | rdpInspector.menu.visitHomePage.tip=详细了解有关 RDP Inspector 8 | rdpInspector.menu.reportIssue.label=报告问题... 9 | rdpInspector.menu.reportIssue.tip=您遇到了 bug?或是有关于新功能的点子?告诉我们吧! 10 | rdpInspector.menu.group.label=讨论组... 11 | rdpInspector.menu.group.tip=打开讨论组网站 (与 Firebug 共享) 12 | -------------------------------------------------------------------------------- /chrome/locale/zh-CN/inspector.properties: -------------------------------------------------------------------------------- 1 | rdpInspector.option.showInlineDetails=显示数据包的详细信息 2 | rdpInspector.option.hideInlineDetails=隐藏数据包的详细信息 3 | rdpInspector.option.cachePackets=在探查器打开前缓存数据包 4 | rdpInspector.cmd.clear=清空 5 | rdpInspector.cmd.find=查找 6 | rdpInspector.cmd.summary=摘要 7 | rdpInspector.tooltip.sizeSent=向调试器服务器发送数据的总量 8 | rdpInspector.tooltip.sizeReceived=从调试器服务器接收数据的总量 9 | rdpInspector.tooltip.packetsSent=向调试器服务器发送的数据包数量 10 | rdpInspector.tooltip.packetsReceived=从调试器服务器接收的数据包数量 11 | rdpInspector.label.packets=数据包 12 | rdpInspector.label.data=数据 13 | rdpInspector.cmd.refresh=刷新 14 | -------------------------------------------------------------------------------- /data/inspector/css/actors-panel.css: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | /******************************************************************************/ 4 | /* Actors Panel */ 5 | 6 | .poolContainer { 7 | font-size: 12px; 8 | } 9 | 10 | .poolTable { 11 | border: 1px solid rgb(221, 221, 221); 12 | width: 100%; 13 | margin-bottom: 10px; 14 | } 15 | 16 | .poolRow TD, 17 | .poolRow TH { 18 | border-bottom: 1px solid rgb(221, 221, 221); 19 | padding-left: 10px; 20 | } 21 | 22 | .poolTable.actorClass { 23 | background-color: rgb(247, 247, 247); 24 | } 25 | -------------------------------------------------------------------------------- /data/inspector/css/search-box.css: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | /******************************************************************************/ 4 | /* Search Box */ 5 | 6 | .searchBox { 7 | height: 21px; 8 | font-size: 12px; 9 | margin-top: 2px; 10 | border: 1px solid rgb(170, 188, 207); 11 | width: 200px; 12 | position: fixed; /* xxxHonza: how to avoid fixed position? */ 13 | right: 5px; 14 | background-image: url("chrome://rdpinspector/skin/search.svg"); 15 | background-repeat: no-repeat; 16 | background-position: 2px center; 17 | padding-left: 20px; 18 | } 19 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "indent": [2, 2], // check 2 space indentation 4 | "no-mixed-spaces-and-tabs": [2], 5 | "quotes": [2, "double"], 6 | "semi": [1, "always"], 7 | 8 | // disabled rule which enforce unix or windows line breaks 9 | "linebreak-style": [0], 10 | 11 | // disabled comma-dangle is safe for our use case 12 | "comma-dangle": [0], 13 | 14 | // disabled eqeqeq and no-use-before-define are safe if you use them correctly 15 | "eqeqeq": [0], 16 | "no-use-before-define": [0], 17 | }, 18 | "env": { 19 | "es6": true 20 | }, 21 | "globals": { 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /data/shared/react-helpers.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | define(function(require, exports/*, module*/) { 4 | 5 | "use strict"; 6 | 7 | const React = require("react"); 8 | 9 | exports.ReactLazyFactories = (reactElementsSource) => { 10 | return new Proxy({}, { 11 | get(target, name) { 12 | if ( !(name in reactElementsSource) ) { 13 | throw Error(`ReactLazyFactories: ${name} not found`); 14 | } 15 | 16 | if ( !(name in target) ) { 17 | target[name] = React.createFactory(reactElementsSource[name]); 18 | } 19 | 20 | return target[name]; 21 | } 22 | }); 23 | }; 24 | 25 | }); 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Junk that could exist anywhere: 3 | .DS_Store 4 | *.swp 5 | *.tmp 6 | .*.gz 7 | *.patch 8 | *~ 9 | 10 | # Temporary files created by Eclipse 11 | .tmp* 12 | 13 | # Editor junk 14 | *.project 15 | /.pydevproject 16 | /.settings/ 17 | /.settings.xml 18 | /.settings.xml.old 19 | /.idea/ 20 | *.iws 21 | *.ids 22 | *.iml 23 | *.ipr 24 | 25 | # Build Files 26 | /build/ 27 | /release/ 28 | *.graphml 29 | *.xpi 30 | 31 | # Files from NPM 32 | /node_modules/ 33 | 34 | # Extensions 35 | /firebug@software.joehewitt.com 36 | 37 | # Bash 38 | *.sh 39 | *.bat 40 | 41 | # code coverage logs & reports 42 | /coverage/ 43 | 44 | bootstrap.js 45 | install.rdf 46 | npm-debug.log 47 | -------------------------------------------------------------------------------- /data/connection-list/reducers.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | define(function(require, exports/*, module*/) { 4 | 5 | "use strict"; 6 | 7 | // actions types 8 | const { types } = require("./actions"); 9 | 10 | /** 11 | * Initial state definition 12 | */ 13 | const initialState = {}; 14 | 15 | function connections(state = initialState, action) { 16 | switch(action.type) { 17 | case types.SET_CONNECTIONS: 18 | return action.connections; 19 | default: 20 | return state; 21 | } 22 | } 23 | 24 | // Export combined reducers 25 | 26 | // Redux 27 | const { combineReducers } = require("redux"); 28 | 29 | exports.rootReducer = combineReducers({ 30 | connections 31 | }); 32 | 33 | }); 34 | -------------------------------------------------------------------------------- /data/inspector/reducers/global.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | define(function(require, exports, module) { 4 | 5 | "use strict"; 6 | 7 | // actions types 8 | const { types } = require("../actions/global"); 9 | 10 | /** 11 | * Initial state definition 12 | */ 13 | const initialState = {}; 14 | 15 | function globalReducer(state = initialState, action) { 16 | switch(action.type) { 17 | case types.SET_SEARCH_FILTER: 18 | return setSearchFilter(state, action.filter); 19 | default: 20 | return state; 21 | } 22 | } 23 | 24 | module.exports = globalReducer; 25 | 26 | 27 | function setSearchFilter(state, filter) { 28 | return Object.assign({}, state, { 29 | searchFilter: filter 30 | }); 31 | } 32 | 33 | }); 34 | -------------------------------------------------------------------------------- /chrome/locale/nl/toolbox.properties: -------------------------------------------------------------------------------- 1 | rdpInspector.title=RDP Inspector 2 | rdpInspector.startButton.title=RDP-inspecteur 3 | rdpInspector.startButton.tip=Protocolinspecteur voor foutopsporing op afstand. 4 | rdpInspector.menu.about.label=Over RDP Inspector… 5 | rdpInspector.menu.about.tip=Informatie over RDP Inspector 6 | rdpInspector.menu.visitHomePage.label=Startpagina bezoeken… 7 | rdpInspector.menu.visitHomePage.tip=Lees meer over RDP Inspector 8 | rdpInspector.menu.reportIssue.label=Probleem melden… 9 | rdpInspector.menu.reportIssue.tip=Hebt u een bug gevonden of zou u graag een nieuwe functie zien? Laat het ons weten! 10 | rdpInspector.menu.group.label=Discussiegroep… 11 | rdpInspector.menu.group.tip=De website van de discussiegroep (gedeeld met Firebug) openen 12 | -------------------------------------------------------------------------------- /chrome/locale/pt-BR/toolbox.properties: -------------------------------------------------------------------------------- 1 | rdpInspector.title=RDP Inspector 2 | rdpInspector.startButton.title=RDP Inspector 3 | rdpInspector.startButton.tip=Inspetor de protocolo de depuração remota. 4 | rdpInspector.menu.about.label=Sobre o RDP Inspector 5 | rdpInspector.menu.about.tip=Informações sobre o RDP Inspector 6 | rdpInspector.menu.visitHomePage.label=Visite a Página Principal… 7 | rdpInspector.menu.visitHomePage.tip=Saiba mais sobre o RDP Inspector 8 | rdpInspector.menu.reportIssue.label=Relatório do problema… 9 | rdpInspector.menu.reportIssue.tip=Encontrou um bug ou precisa de um novo recurso? Nos informe! 10 | rdpInspector.menu.group.label=Grupo de discussão… 11 | rdpInspector.menu.group.tip=Abrir o site do grupo de discussão (compartilhado com o Firebug) 12 | -------------------------------------------------------------------------------- /karma-tests/custom-react-matchers.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | define(function(require, exports, module) { 4 | 5 | "use strict"; 6 | 7 | var React = require("react"); 8 | var { TestUtils } = React.addons; 9 | 10 | module.exports = { 11 | toBeFoundInReactTree: () => { 12 | return { 13 | compare: (component, tree, length) => { 14 | var message = "React Component '" + component.displayName + "'" + 15 | " not found in the rendered tree"; 16 | 17 | var instances = TestUtils.scryRenderedComponentsWithType(tree, component); 18 | 19 | return { 20 | pass: length ? 21 | instances.length === length : instances.length > 0, 22 | message: message 23 | }; 24 | } 25 | }; 26 | } 27 | }; 28 | 29 | }); 30 | -------------------------------------------------------------------------------- /chrome.manifest: -------------------------------------------------------------------------------- 1 | content rdpinspector chrome/content/ 2 | skin rdpinspector classic/1.0 chrome/skin/classic/shared/ 3 | skin rdpinspector-firebug.sdk classic/1.0 node_modules/firebug.sdk/skin/classic/shared/ 4 | 5 | locale rdpinspector en-US chrome/locale/en-US/ 6 | locale rdpinspector zh-CN chrome/locale/zh-CN/ 7 | locale rdpinspector nl chrome/locale/nl/ 8 | locale rdpinspector pt-BR chrome/locale/pt-BR/ 9 | 10 | locale rdpinspector-firebug.sdk en-US node_modules/firebug.sdk/locale/en-US/ 11 | locale rdpinspector-firebug.sdk zh-CN node_modules/firebug.sdk/locale/zh-CN/ 12 | locale rdpinspector-firebug.sdk nl node_modules/firebug.sdk/locale/nl/ 13 | locale rdpinspector-firebug.sdk pt-BR node_modules/firebug.sdk/locale/pt-BR/ 14 | -------------------------------------------------------------------------------- /data/inspector/search.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | define(function(require, exports/*, module*/) { 4 | 5 | "use strict"; 6 | 7 | const actions = require("./actions/index"); 8 | 9 | /** 10 | * This object is responsible for listening search events 11 | * and updating application state (the root RJS component). 12 | */ 13 | function Search(win, store) { 14 | this.win = win; 15 | this.store = store; 16 | 17 | this.win.addEventListener("search", this.onSearch.bind(this)); 18 | } 19 | 20 | Search.prototype = 21 | /** @lends Search */ 22 | { 23 | onSearch: function(event) { 24 | var value = event.data; 25 | this.store.dispatch(actions.setSearchFilter(value)); 26 | } 27 | }; 28 | 29 | // Exports from this module 30 | exports.Search = Search; 31 | }); 32 | -------------------------------------------------------------------------------- /chrome/locale/pt-BR/inspector.properties: -------------------------------------------------------------------------------- 1 | rdpInspector.option.showInlineDetails=Exibir detalhes do pacote 2 | rdpInspector.option.hideInlineDetails=Ocultar detalhes do pacote 3 | rdpInspector.option.cachePackets=Pacotes da cache antes do inspector ser aberto 4 | rdpInspector.cmd.clear=Limpar 5 | rdpInspector.cmd.find=Procurar 6 | rdpInspector.cmd.summary=Resumo 7 | rdpInspector.tooltip.sizeSent=A quantidade total de dados enviado para o servidor depurador 8 | rdpInspector.tooltip.sizeReceived=Quantidade total de dados recebidos do servidor depurador 9 | rdpInspector.tooltip.packetsSent=O número de pacotes enviada para o servidor depurador 10 | rdpInspector.tooltip.packetsReceived=Número de pacotes recebidos a partir do servidor depurador 11 | rdpInspector.label.packets=Pacotes 12 | rdpInspector.label.data=Dados 13 | rdpInspector.cmd.refresh=Recarregar 14 | -------------------------------------------------------------------------------- /data/shared/resizer.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | define(function(require, exports/*, module*/) { 4 | 5 | "use strict"; 6 | 7 | /** 8 | * This object is responsible for setting proper body height 9 | * when the window changes its size. 10 | */ 11 | function Resizer(win, app) { 12 | this.win = win; 13 | this.app = app; 14 | this.win.addEventListener("resize", this.onResize.bind(this)); 15 | 16 | // Initialize content size 17 | this.onResize(); 18 | } 19 | 20 | Resizer.prototype = 21 | /** @lends Resizer */ 22 | { 23 | onResize: function() { 24 | var doc = this.win.document; 25 | doc.body.style.height = this.win.innerHeight + "px"; 26 | doc.body.style.width = this.win.innerWidth + "px"; 27 | } 28 | }; 29 | 30 | // Exports from this module 31 | exports.Resizer = Resizer; 32 | }); 33 | -------------------------------------------------------------------------------- /data/inspector/config.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | /* globals requirejs */ 4 | 5 | // RequireJS configuration 6 | require.config({ 7 | baseUrl: ".", 8 | scriptType: "application/javascript;version=1.8", 9 | paths: { 10 | "shared": "../shared", 11 | "jquery": "../lib/jquery/jquery", 12 | "react": "../lib/react/react", 13 | "react-dom": "../lib/react/react-dom", 14 | "bootstrap": "../lib/bootstrap/js/bootstrap", 15 | "immutable": "../lib/immutable/immutable", 16 | "react-bootstrap": "../lib/react-bootstrap/react-bootstrap", 17 | "reps": "../../node_modules/firebug.sdk/lib/reps", 18 | "firebug.sdk": "../../node_modules/firebug.sdk", 19 | "redux": "../lib/redux/redux", 20 | "react-redux": "../lib/redux/react-redux", 21 | } 22 | }); 23 | 24 | // Load the main panel module 25 | requirejs(["main"]); 26 | -------------------------------------------------------------------------------- /chrome/locale/nl/inspector.properties: -------------------------------------------------------------------------------- 1 | rdpInspector.option.showInlineDetails=Pakketdetails inline weergeven 2 | rdpInspector.option.hideInlineDetails=Pakketdetails inline verbergen 3 | rdpInspector.option.cachePackets=Pakketten bufferen voordat de inspecteur wordt geopend 4 | rdpInspector.cmd.clear=Wissen 5 | rdpInspector.cmd.find=Zoeken 6 | rdpInspector.cmd.summary=Samenvatting 7 | rdpInspector.tooltip.sizeSent=Totaal aantal naar de foutopsporingsserver verstuurde gegevens 8 | rdpInspector.tooltip.sizeReceived=Totaal aantal van de foutopsporingsserver ontvangen gegevens 9 | rdpInspector.tooltip.packetsSent=Aantal naar de foutopsporingsserver verstuurde pakketten 10 | rdpInspector.tooltip.packetsReceived=Aantal van de foutopsporingsserver ontvangen pakketten 11 | rdpInspector.label.packets=Pakketten 12 | rdpInspector.label.data=Gegevens 13 | rdpInspector.cmd.refresh=Vernieuwen 14 | -------------------------------------------------------------------------------- /data/connection-list/config.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | /* globals requirejs */ 4 | 5 | // RequireJS configuration 6 | require.config({ 7 | baseUrl: ".", 8 | scriptType: "application/javascript;version=1.8", 9 | paths: { 10 | "shared": "../shared", 11 | "jquery": "../lib/jquery/jquery", 12 | "react": "../lib/react/react", 13 | "react-dom": "../lib/react/react-dom", 14 | "bootstrap": "../lib/bootstrap/js/bootstrap", 15 | "immutable": "../lib/immutable/immutable", 16 | "react-bootstrap": "../lib/react-bootstrap/react-bootstrap", 17 | "reps": "../../node_modules/firebug.sdk/lib/reps", 18 | "firebug.sdk": "../../node_modules/firebug.sdk", 19 | "redux": "../lib/redux/redux", 20 | "react-redux": "../lib/redux/react-redux", 21 | } 22 | }); 23 | 24 | // Load the main panel module 25 | requirejs(["index"]); 26 | -------------------------------------------------------------------------------- /data/connection-list/containers/main-panel.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | define(function(require, exports/*, module*/) { 4 | 5 | "use strict"; 6 | 7 | // React & Redux 8 | const React = require("react"); 9 | const { connect } = require("react-redux"); 10 | 11 | // Connections List components 12 | const { ConnectionList } = require("../components/connection-list"); 13 | 14 | const MainPanel = React.createClass({ 15 | displayName: "MainPanel", 16 | 17 | render() { 18 | return React.createElement(ConnectionList, { 19 | connections: this.props.connections, 20 | onConnectionClick: this.props.onConnectionClick 21 | }); 22 | } 23 | }); 24 | 25 | function mapStoreToProps(state) { 26 | let { connections } = state; 27 | return { connections }; 28 | } 29 | 30 | exports.MainPanel = connect(mapStoreToProps)(MainPanel); 31 | 32 | }); 33 | -------------------------------------------------------------------------------- /data/inspector/containers/app.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | define(function(require, exports/*, module*/) { 4 | 5 | "use strict"; 6 | 7 | // React & Redux 8 | const React = require("react"); 9 | const { connect } = require("react-redux"); 10 | 11 | // Connections List components 12 | const MainTabbedArea = require("../components/main-tabbed-area"); 13 | 14 | const App = React.createClass({ 15 | displayName: "App", 16 | 17 | render() { 18 | return React.createElement(MainTabbedArea, { 19 | view: this.props.view, 20 | global: this.props.global, 21 | actors: this.props.actors, 22 | packets: this.props.packets, 23 | }); 24 | } 25 | }); 26 | 27 | function mapStoreToProps(state) { 28 | let { global, actors, packets } = state; 29 | return { global, actors, packets }; 30 | } 31 | 32 | exports.App = connect(mapStoreToProps)(App); 33 | 34 | }); 35 | -------------------------------------------------------------------------------- /data/.eslintrc: -------------------------------------------------------------------------------- 1 | // extends common eslintrc config with "javascript running in a content context" specific configs 2 | { 3 | "extends": "../.eslintrc", 4 | "plugins": [ 5 | // prevents tab whitespace when 'indent' rule is disabled 6 | "no-tabs", 7 | "react" 8 | ], 9 | "env": { 10 | // enable browser & amd conventions 11 | "browser": true, 12 | "amd": true 13 | }, 14 | "rules": { 15 | // disabled due to conflicts with special indentation in requirejs modules 16 | "indent": 0, 17 | // prevents any usage of tab whitespaces 18 | "no-tabs/at-all": [2], 19 | 20 | // force definition of displayName on React components 21 | "react/display-name": 2, 22 | 23 | // TODO: force propTypes on React Components 24 | "react/prop-types": 0, 25 | 26 | // disabled due to conflicts with capitalized React components names 27 | "new-cap": [0], 28 | }, 29 | "globals": {} 30 | } 31 | -------------------------------------------------------------------------------- /data/connection-list/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /karma-tests/lib/firebug-sdk-shims.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | (function (window, document) { 4 | 5 | "use strict"; 6 | 7 | // set the theme-firebug class on the body element 8 | document.body.setAttribute("class", "theme-firebug"); 9 | 10 | // bootstrap, firebug.sdk and rdp-inspector stylesheets 11 | var stylesheets = [ 12 | "../lib/bootstrap/css/bootstrap.css", 13 | "css/base.css", 14 | "css/toolbox.css", 15 | "css/toolbar.css", 16 | "css/packets-panel.css", 17 | "css/actors-panel.css", 18 | "css/search-box.css", 19 | "css/tree-editor-view.css", 20 | "../../node_modules/firebug.sdk/skin/classic/shared/domTree.css", 21 | "../../node_modules/firebug.sdk/skin/classic/shared/splitter.css" 22 | ]; 23 | 24 | stylesheets.forEach(function(url) { 25 | var linkEl = document.createElement("link"); 26 | linkEl.setAttribute("href", "/base/data/inspector/" + url); 27 | linkEl.setAttribute("rel", "stylesheet"); 28 | document.head.appendChild(linkEl); 29 | }) 30 | 31 | })(window, document); 32 | -------------------------------------------------------------------------------- /data/inspector/components/text-with-tooltip.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | define(function(require, exports/*, module*/) { 4 | 5 | "use strict"; 6 | 7 | // ReactJS 8 | const React = require("react"); 9 | 10 | // Constants 11 | const { Tooltip, OverlayTrigger } = require("shared/react-bootstrap-factories"); 12 | 13 | const { div } = React.DOM; 14 | 15 | /** 16 | * @template Helper template responsible for rendering a tooltip. 17 | */ 18 | const TextWithTooltip = React.createFactory(React.createClass({ 19 | displayName: "TextWithTooltip", 20 | 21 | render: function() { 22 | var tooltip = Tooltip({}, this.props.tooltip); 23 | return ( 24 | OverlayTrigger({placement: "top", overlay: tooltip, delayShow: 200, 25 | delayHide: 150}, 26 | div({className: this.props.className, onClick: this.props.onClick}, 27 | this.props.children 28 | ) 29 | ) 30 | ); 31 | } 32 | })); 33 | 34 | // Exports from this module 35 | exports.TextWithTooltip = TextWithTooltip; 36 | }); 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | RDP Inspector 2 | ============= 3 | Remote debugger protocol inspector 4 | 5 | Instructions 6 | ------------ 7 | You should see a new RDP Inspector button at the top-right corner of your 8 | browser window after installation. Click the icon to open RDP Inspector 9 | console and operate developer tools Toolbox to generate some traffic 10 | over RDP. Sent and received packets are visible in the console window. 11 | 12 | * [Learn more](https://github.com/firebug/rdp-inspector/wiki) 13 | * [Download](https://github.com/firebug/rdp-inspector/releases) 14 | 15 | Further Resources 16 | ----------------- 17 | * Remote Debugging Protocol: https://wiki.mozilla.org/Remote_Debugging_Protocol 18 | * Add-on SDK: https://developer.mozilla.org/en-US/Add-ons/SDK 19 | * DevTools API: https://developer.mozilla.org/en-US/docs/Tools/DevToolsAPI 20 | * Coding Style: https://github.com/mozilla/addon-sdk/wiki/Coding-style-guide 21 | * DevTools Extension Examples: https://github.com/firebug/devtools-extension-examples 22 | * DevTools/Hacking: https://wiki.mozilla.org/DevTools/Hacking 23 | -------------------------------------------------------------------------------- /data/inspector/css/toolbar.css: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | /******************************************************************************/ 4 | /* Toolbar */ 5 | 6 | .toolbar { 7 | height: 30px; 8 | padding: 4px; 9 | border-bottom: 1px solid rgb(170, 188, 207); 10 | background-color: rgb(219, 234, 249) !important; 11 | background-image: linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.2)); 12 | } 13 | 14 | .toolbar .btn { 15 | border-radius: 2px; 16 | color: #141414; 17 | } 18 | 19 | .toolbar select { 20 | margin-left: 3px; 21 | line-height: 1.0; 22 | font-size: 12px; 23 | } 24 | 25 | .toolbar input[type="checkbox"] { 26 | margin-right: 8px; 27 | } 28 | 29 | /******************************************************************************/ 30 | /* Tooltip */ 31 | 32 | .tooltip .tooltip-inner { 33 | background-color: #EEE1B3; 34 | color: black; 35 | max-width: 300px; 36 | } 37 | .tooltip.bottom .tooltip-arrow { 38 | border-bottom-color: #EEE1B3; 39 | color: black; 40 | } 41 | -------------------------------------------------------------------------------- /data/inspector/components/packets-message.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | define(function(require, exports/*, module*/) { 4 | 5 | "use strict"; 6 | 7 | // ReactJS 8 | const React = require("react"); 9 | 10 | // Constants 11 | const { div } = React.DOM; 12 | 13 | /** 14 | * @template This template renders a generic message within the 15 | * packet list. 16 | */ 17 | var PacketsMessage = React.createClass({ 18 | /** @lends PacketsMessage */ 19 | 20 | displayName: "PacketsMessage", 21 | 22 | render: function() { 23 | var packet = this.props.data; 24 | var message = packet.message; 25 | var time = packet.time; 26 | 27 | var timeText = time.toLocaleTimeString() + "." + time.getMilliseconds(); 28 | 29 | // Render summary info 30 | return ( 31 | div({className: "packetsMessage"}, 32 | div({className: "text"}, message), 33 | div({className: "time"}, timeText) 34 | ) 35 | ); 36 | } 37 | }); 38 | 39 | // Exports from this module 40 | exports.PacketsMessage = React.createFactory(PacketsMessage); 41 | }); 42 | -------------------------------------------------------------------------------- /data/inspector/components/packet-details.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | define(function(require, exports/*, module*/) { 4 | 5 | "use strict"; 6 | 7 | // Dependencies 8 | const React = require("react"); 9 | 10 | // Firebug SDK 11 | const { TreeView } = require("reps/tree-view"); 12 | 13 | // Shortcuts 14 | const { div } = React.DOM; 15 | 16 | /** 17 | * @template This template represents a list of packets displayed 18 | * inside the panel content. 19 | */ 20 | var PacketDetails = React.createClass({ 21 | /** @lends PacketDetails */ 22 | 23 | displayName: "PacketDetails", 24 | 25 | getInitialState: function() { 26 | return { 27 | selectedPacket: null 28 | }; 29 | }, 30 | 31 | render: function() { 32 | var selectedPacket = this.props.selectedPacket || {}; 33 | 34 | return ( 35 | div({className: "details"}, 36 | TreeView({key: "packet-detail", data: selectedPacket.packet}) 37 | ) 38 | ); 39 | } 40 | }); 41 | 42 | // Exports from this module 43 | exports.PacketDetails = React.createFactory(PacketDetails); 44 | }); 45 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - "0.10" 5 | env: 6 | ## Firefox Developer Edition 7 | - FIREFOX_VERSION=aurora-latest 8 | - FIREFOX_VERSION=fx-team 9 | 10 | before_install: 11 | - "export DISPLAY=:99.0" 12 | - "sh -e /etc/init.d/xvfb start" 13 | - "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16 -extension RANDR" 14 | - npm install -g npm 15 | - npm --version 16 | 17 | before_script: 18 | - npm install jpm -g 19 | - '( [[ "$FIREFOX_VERSION" = "fx-team" ]] && npm install mozilla-download -g && mozilla-download --branch fx-team --product firefox $TRAVIS_BUILD_DIR/../ ) || echo "Building on release $FIREFOX_VERSION"' 20 | - '( [[ "$FIREFOX_VERSION" != "fx-team" ]] && wget "https://download.mozilla.org/?product=firefox-$FIREFOX_VERSION-ssl&os=linux64&lang=en-US" -O $TRAVIS_BUILD_DIR/../firefox.tar.bz2 && cd $TRAVIS_BUILD_DIR/../ && tar xvf firefox.tar.bz2 ) || echo "Building on release $FIREFOX_VERSION"' 21 | - cd $TRAVIS_BUILD_DIR 22 | 23 | script: 24 | - export JPM_FIREFOX_BINARY=$TRAVIS_BUILD_DIR/../firefox/firefox 25 | - export FIREFOX_BIN=$JPM_FIREFOX_BINARY 26 | - npm run travis-ci 27 | - npm run jpm-tests 28 | -------------------------------------------------------------------------------- /data/inspector/components/packets-limit.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | define(function(require, exports/*, module*/) { 4 | 5 | "use strict"; 6 | 7 | // ReactJS 8 | const React = require("react"); 9 | 10 | // Constants 11 | const { div, span } = React.DOM; 12 | 13 | // RDP Window injected APIs 14 | const { Locale } = require("shared/rdp-inspector-window"); 15 | 16 | /** 17 | * @template This template is responsible for rendering a message 18 | * at the top of the packet list that informs the user about reaching 19 | * the maximum limit of displayed packets. The message also displays 20 | * number of packets removed from the list. 21 | */ 22 | var PacketsLimit = React.createClass({ 23 | /** @lends PacketsLimit */ 24 | 25 | displayName: "PacketsLimit", 26 | 27 | render: function() { 28 | var removedPackets = this.props.removedPackets; 29 | var label = Locale.$STR("rdpInspector.label.outOfLimit"); 30 | 31 | // Render summary info 32 | return ( 33 | div({className: "packetsLimit"}, 34 | span({className: "text"}, removedPackets + " " + label) 35 | ) 36 | ); 37 | } 38 | }); 39 | 40 | // Exports from this module 41 | exports.PacketsLimit = React.createFactory(PacketsLimit); 42 | }); 43 | -------------------------------------------------------------------------------- /data/inspector/main.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | "use strict"; 4 | 5 | /** 6 | * This file is specified as the 'main' module in package.json 7 | * MDN: https://developer.mozilla.org/en-US/Add-ons/SDK/Tools/package_json 8 | */ 9 | let FBTrace = require("firebug.sdk/lib/core/trace.js").FBTrace; 10 | let { main, Loader, override } = require("toolkit/loader"); 11 | 12 | // Get default loader options and create one new 'FBTrace' global, 13 | // so it's automatically available in every loaded module. 14 | // Note that FBTrace global should be used for debugging purposes only. 15 | let options = require("@loader/options"); 16 | 17 | let defaultGlobals = override(require("sdk/system/globals"), { 18 | FBTrace: FBTrace 19 | }); 20 | 21 | options = override(options, { 22 | globals: defaultGlobals, 23 | 24 | // See also: https://bugzilla.mozilla.org/show_bug.cgi?id=1123268 25 | modules: override(options.modules || {}, { 26 | "sdk/addon/window": require("sdk/addon/window") 27 | }) 28 | }); 29 | 30 | // Create custom loader with modified options. 31 | let loader = Loader(options); 32 | let program = main(loader, "./lib/main.js"); 33 | 34 | // Exports from this module 35 | exports.main = program.main; 36 | exports.onUnload = program.onUnload; 37 | -------------------------------------------------------------------------------- /data/lib/react/react-dom.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ReactDOM v0.14.2 3 | * 4 | * Copyright 2013-2015, Facebook, Inc. 5 | * All rights reserved. 6 | * 7 | * This source code is licensed under the BSD-style license found in the 8 | * LICENSE file in the root directory of this source tree. An additional grant 9 | * of patent rights can be found in the PATENTS file in the same directory. 10 | * 11 | */ 12 | // Based off https://github.com/ForbesLindesay/umd/blob/master/template.js 13 | ;(function(f) { 14 | // CommonJS 15 | if (typeof exports === "object" && typeof module !== "undefined") { 16 | module.exports = f(require('react')); 17 | 18 | // RequireJS 19 | } else if (typeof define === "function" && define.amd) { 20 | define(['react'], f); 21 | 22 | // 48 | 49 | 50 | -------------------------------------------------------------------------------- /chrome/content/inspector-window.xul: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 14 | 15 | 16 | 46 | 47 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /data/inspector/actions/packets.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | define(function(require, exports, module) { 4 | 5 | "use strict"; 6 | 7 | const types = { 8 | IMPORT_PACKETS_CACHE: "IMPORT_PACKETS_CACHE", 9 | IMPORT_PACKETS_FROMFILE: "IMPORTS_PACKETS_FROMFILE", 10 | INIT_PACKETLIST_OPTIONS: "INIT_PACKETLIST_OPTIONS", 11 | APPEND_PACKET: "APPEND_PACKET", 12 | APPEND_SUMMARY: "APPEND_SUMMARY", 13 | APPEND_MESSAGE: "APPEND_MESSAGE", 14 | EDIT_PACKET: "EDIT_PACKET", 15 | SELECT_PACKET: "SELECT_PACKET", 16 | CLEAR_PACKET_LIST: "CLEAR_PACKET_LIST", 17 | SET_PACKET_FILTER: "SET_PACKET_FILTER", 18 | TOGGLE_PACKETLIST_OPTION: "TOGGLE_PACKETLIST_OPTION", 19 | TOGGLE_PACKETLIST_PAUSE: "TOGGLE_PACKETLIST_PAUSE", 20 | SET_PACKETLIST_ERROR: "SET_PACKETLIST_ERROR", 21 | }; 22 | 23 | // export actions types and action creators 24 | module.exports = { 25 | types, 26 | 27 | importPacketsCache(cache) { 28 | return { type: types.IMPORT_PACKETS_CACHE, cache }; 29 | }, 30 | 31 | importPacketsFromFile(data) { 32 | return { type: types.IMPORT_PACKETS_FROMFILE, data }; 33 | }, 34 | 35 | initPacketListOptions(options) { 36 | return { type: types.INIT_PACKETLIST_OPTIONS, options }; 37 | }, 38 | 39 | appendPacket(packetType, packetData) { 40 | return { type: types.APPEND_PACKET, packetType, packetData }; 41 | }, 42 | 43 | appendSummary() { 44 | let time = new Date(); 45 | return { type: types.APPEND_SUMMARY, time }; 46 | }, 47 | 48 | appendMessage(message) { 49 | let time = new Date(); 50 | return { type: types.APPEND_MESSAGE, message, time }; 51 | }, 52 | 53 | editPacket(packet) { 54 | return { type: types.EDIT_PACKET, packet }; 55 | }, 56 | 57 | selectPacket(packet) { 58 | return { type: types.SELECT_PACKET, packet }; 59 | }, 60 | 61 | clearPacketList() { 62 | return { type: types.CLEAR_PACKET_LIST }; 63 | }, 64 | 65 | togglePacketListOption(name) { 66 | return { type: types.TOGGLE_PACKETLIST_OPTION, name }; 67 | }, 68 | 69 | togglePacketListPause() { 70 | return { type: types.TOGGLE_PACKETLIST_PAUSE }; 71 | }, 72 | 73 | setPacketListError(error) { 74 | return { type: types.SET_PACKETLIST_ERROR, error }; 75 | } 76 | 77 | }; 78 | 79 | }); 80 | -------------------------------------------------------------------------------- /test/test-inspector-service.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const { InspectorService } = require("../lib/inspector-service"); 4 | 5 | const { getMostRecentBrowserWindow } = require("sdk/window/utils"); 6 | 7 | // DevTools 8 | const { devtools, gDevTools } = require("firebug.sdk/lib/core/devtools.js"); 9 | 10 | function showToolbox(toolId) { 11 | let browser = getMostRecentBrowserWindow(); 12 | let tab = browser.gBrowser.mCurrentTab; 13 | let target = devtools.TargetFactory.forTab(tab); 14 | return gDevTools.showToolbox(target, toolId); 15 | } 16 | 17 | function formatException(e) { 18 | return `\n\t${e.fileName}:${e.lineNumber}\n\t${e}`; 19 | } 20 | 21 | exports["test InspectorService"] = function(assert, done) { 22 | showToolbox("webconsole").then((toolbox) => { 23 | assert.ok(toolbox, "toolbox should be defined"); 24 | 25 | InspectorService.getInspectorClients(toolbox).then((inspectorClients) => { 26 | assert.ok(inspectorClients, "inspectorClients should be defined"); 27 | assert.deepEqual(Object.keys(inspectorClients), ["global", "tab"], 28 | "global and tab keys should be defined"); 29 | let { global, tab } = inspectorClients; 30 | assert.equal(typeof global.getActors, "function", "global.getActors function is defined"); 31 | assert.equal(typeof tab.getActors, "function", "tab.getActors function is defined"); 32 | 33 | Promise.all([global.getActors(), tab.getActors()]).then(([tabActors, globalActors]) => { 34 | const EXPECTED_ACTOR_KEYS = ["actorPool", "extraPools", "factories", "from"]; 35 | 36 | assert.deepEqual(Object.keys(globalActors), EXPECTED_ACTOR_KEYS, "globalActors should have the expected attributes"); 37 | assert.deepEqual(Object.keys(tabActors), EXPECTED_ACTOR_KEYS, "tabActors should have the expected attributes"); 38 | 39 | done(); 40 | }).catch((e) => { 41 | assert.fail(`Exception catched during getActors: ${formatException(e)}`); 42 | done(); 43 | }); 44 | }).catch((e) => { 45 | assert.fail(`Exception catched during getInspectorClients: ${formatException(e)}`); 46 | done(); 47 | }); 48 | }).catch((e) => { 49 | assert.fail(`Exception catched during showToolbox: ${formatException(e)}`); 50 | done(); 51 | }); 52 | }; 53 | 54 | require("sdk/test").run(exports); 55 | -------------------------------------------------------------------------------- /data/inspector/components/packets-panel.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | define(function(require, exports/*, module*/) { 4 | 5 | "use strict"; 6 | 7 | // Dependencies 8 | const React = require("react"); 9 | 10 | // Firebug SDK 11 | const { createFactories } = require("reps/rep-utils"); 12 | const { Splitter } = createFactories(require("reps/splitter")); 13 | 14 | // RDP Inspector 15 | const { PacketList } = require("./packet-list"); 16 | const { PacketsSidebar } = require("./packets-sidebar"); 17 | const { PacketsToolbar } = require("./packets-toolbar"); 18 | 19 | // Shortcuts 20 | const { div } = React.DOM; 21 | 22 | /** 23 | * @template This template renders 'Packets' tab body. 24 | */ 25 | var PacketsPanel = React.createClass({ 26 | /** @lends PacketPanel */ 27 | 28 | displayName: "PacketsPanel", 29 | 30 | getInitialState: function() { 31 | return { 32 | packets: this.props.packets, 33 | selectedPacket: null 34 | }; 35 | }, 36 | 37 | render: function() { 38 | var leftPanel = div({className: "mainPanel"}, 39 | PacketsToolbar({ 40 | actions: this.props.actions, 41 | showInlineDetails: this.props.showInlineDetails, 42 | packetCacheEnabled: this.props.packetCacheEnabled, 43 | paused: this.props.paused 44 | }), 45 | PacketList({ 46 | data: this.props.packets, 47 | actions: this.props.actions, 48 | selectedPacket: this.props.selectedPacket, 49 | searchFilter: this.props.searchFilter, 50 | showInlineDetails: this.props.showInlineDetails, 51 | removedPackets: this.props.removedPackets 52 | }) 53 | ); 54 | 55 | var rightPanel = div({className: "sidePanel"}, 56 | PacketsSidebar({ 57 | selectedPacket: this.props.selectedPacket, 58 | editedPacket: this.props.editedPacket, 59 | actions: this.props.actions, 60 | actorIDs: this.props.actorIDs 61 | }) 62 | ); 63 | 64 | return ( 65 | div({className: "packetsPanelBox"}, 66 | Splitter({ 67 | mode: "vertical", 68 | min: 200, 69 | leftPanel: leftPanel, 70 | rightPanel: rightPanel, 71 | innerBox: div({className: "innerBox"}) 72 | }) 73 | ) 74 | ); 75 | } 76 | }); 77 | 78 | // Exports from this module 79 | exports.PacketsPanel = React.createFactory(PacketsPanel); 80 | exports.PacketsPanelComponent = PacketsPanel; 81 | 82 | }); 83 | -------------------------------------------------------------------------------- /data/inspector/components/packets-sidebar.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | define(function(require, exports/*, module*/) { 4 | 5 | "use strict"; 6 | 7 | // ReactJS 8 | const React = require("react"); 9 | 10 | // RDP Inspector 11 | const { PacketDetails } = require("./packet-details"); 12 | const { PacketEditor } = require("./packet-editor"); 13 | const { PacketStackSidePanel } = require("./packet-stack-sidepanel"); 14 | 15 | // Constants 16 | const { Tabs, Tab } = require("shared/react-bootstrap-factories"); 17 | 18 | /** 19 | * @template This template represents a list of packets displayed 20 | * inside the panel content. 21 | */ 22 | var PacketsSidebar = React.createClass({ 23 | /** @lends PacketsSidebar */ 24 | 25 | displayName: "PacketsSidebar", 26 | 27 | getInitialState: function() { 28 | return { 29 | activeKey: 1 30 | }; 31 | }, 32 | 33 | onTabSelected: function(key) { 34 | this.setState({ 35 | activeKey: key 36 | }); 37 | }, 38 | 39 | componentDidMount: function() { 40 | window.addEventListener("rdpinspector:switchToPacketEditorTab", 41 | this.onSwitchToPacketEditorTab); 42 | }, 43 | 44 | componentWillUnmount: function() { 45 | window.removeEventListener("rdpinspector:switchToPacketEditorTab", this.onSwitchToPacketEditorTab); 46 | }, 47 | 48 | onSwitchToPacketEditorTab: function() { 49 | this.setState({ activeKey: 2 }); 50 | }, 51 | 52 | render: function() { 53 | // TODO xxxRpl: externalize and localize sidebar tab titles 54 | return ( 55 | Tabs({ 56 | className: "sideBarTabbedArea", animation: false, 57 | activeKey: this.state.activeKey, 58 | onSelect: this.onTabSelected 59 | }, 60 | Tab({ eventKey: 1, title: "Packet Details" }, 61 | PacketDetails({ 62 | selectedPacket: this.props.selectedPacket 63 | }) 64 | ), 65 | Tab({ eventKey: 2, title: "Send Packet" }, 66 | PacketEditor({ 67 | actions: this.props.actions, 68 | actorIDs: this.props.actorIDs, 69 | editedPacket: this.props.editedPacket 70 | }) 71 | ), 72 | Tab({ eventKey: 3, title: "Stack" }, 73 | PacketStackSidePanel({ 74 | actions: this.props.actions, 75 | selectedPacket: this.props.selectedPacket 76 | }) 77 | ) 78 | ) 79 | ); 80 | } 81 | }); 82 | 83 | // Exports from this module 84 | exports.PacketsSidebar = React.createFactory(PacketsSidebar); 85 | exports.PacketsSidebarComponent = PacketsSidebar; 86 | }); 87 | -------------------------------------------------------------------------------- /data/inspector/components/packets-summary.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | define(function(require, exports/*, module*/) { 4 | 5 | "use strict"; 6 | 7 | // ReactJS 8 | const React = require("react"); 9 | 10 | // RDP Inspector 11 | const { TextWithTooltip } = require("./text-with-tooltip"); 12 | 13 | // RDP Window injected APIs 14 | const { Locale, Str } = require("shared/rdp-inspector-window"); 15 | 16 | 17 | // Constants 18 | const { div } = React.DOM; 19 | 20 | /** 21 | * @template This template is responsible for rendering packet summary 22 | * information. The summary can be inserted into the list by clicking 23 | * on 'Summary' button. It displays number of sent and received packets 24 | * and total amount of sent and received data. 25 | */ 26 | var PacketsSummary = React.createClass({ 27 | /** @lends PacketsSummary */ 28 | 29 | displayName: "PacketsSummary", 30 | 31 | render: function() { 32 | var summary = this.props.data; 33 | var time = summary.time; 34 | 35 | var timeText = time.toLocaleTimeString() + "." + time.getMilliseconds(); 36 | var sizeReceived = Str.formatSize(summary.data.received); 37 | var sizeSent = Str.formatSize(summary.data.sent); 38 | 39 | // Tooltips 40 | var sizeSentTip = Locale.$STR("rdpInspector.tooltip.sizeSent"); 41 | var sizeReceivedTip = Locale.$STR("rdpInspector.tooltip.sizeReceived"); 42 | var packetsSentTip = Locale.$STR("rdpInspector.tooltip.packetsSent"); 43 | var packetsReceivedTip = Locale.$STR("rdpInspector.tooltip.packetsReceived"); 44 | 45 | // Labels 46 | var packetsText = Locale.$STR("rdpInspector.label.packets"); 47 | var dataText = Locale.$STR("rdpInspector.label.data"); 48 | 49 | // Render summary info 50 | return ( 51 | div({className: "packetsSummary"}, 52 | div({className: "text"}, packetsText + ": "), 53 | TextWithTooltip({tooltip: packetsSentTip}, summary.packets.sent), 54 | div({className: "slash"}, " / "), 55 | TextWithTooltip({tooltip: packetsReceivedTip}, summary.packets.received), 56 | div({className: "separator"}), 57 | div({className: "text"}, dataText + ": "), 58 | TextWithTooltip({tooltip: sizeSentTip}, sizeSent), 59 | div({className: "slash"}, " / "), 60 | TextWithTooltip({tooltip: sizeReceivedTip}, sizeReceived), 61 | div({className: "separator"}), 62 | div({className: "time"}, timeText) 63 | ) 64 | ); 65 | } 66 | }); 67 | 68 | // Exports from this module 69 | exports.PacketsSummary = React.createFactory(PacketsSummary); 70 | exports.PacketsSummaryComponent = PacketsSummary; 71 | }); 72 | -------------------------------------------------------------------------------- /data/inspector/res/arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 16 | 18 | 22 | 26 | 27 | 29 | 33 | 37 | 38 | 47 | 56 | 57 | 59 | 60 | 62 | image/svg+xml 63 | 65 | 66 | 67 | 68 | 69 | 72 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /lib/webide-connections-monitor.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | "use strict"; 4 | 5 | module.metadata = { 6 | "stability": "extermental" 7 | }; 8 | 9 | // Firebug SDK 10 | const { Dispatcher } = require("firebug.sdk/lib/dispatcher"); 11 | const { Trace/*, TraceError*/ } = require("firebug.sdk/lib/core/trace.js").get(module.id); 12 | 13 | // DevTools 14 | // See also: https://bugzilla.mozilla.org/show_bug.cgi?id=912121 15 | const { devtools, safeRequire } = require("firebug.sdk/lib/core/devtools"); 16 | const { 17 | ConnectionManager, Connection 18 | } = safeRequire(devtools, "devtools/client/connection-manager", "devtools/shared/client/connection-manager"); 19 | const { 20 | AppManager 21 | } = safeRequire(devtools, "devtools/webide/app-manager", "devtools/client/webide/modules/app-manager"); 22 | 23 | /** 24 | * @service This service monitors the connections created by WebIDE and dispatch the 25 | * webide connection lifecycle events, which are used by the InspectorService to keep 26 | * track of the existent debugger clients (and their related toolboxes). 27 | */ 28 | const WebIDEConnectionsMonitor = 29 | /** @lends WebIDEConnectionsMonitor */ 30 | { 31 | // Initialization 32 | 33 | initialize: function() { 34 | Trace.sysout("WebIDEConnectionsMonitor.initialize;", arguments); 35 | 36 | this.onNewConnection = this.onNewConnection.bind(this); 37 | this.onDestroyConnection = this.onDestroyConnection.bind(this); 38 | 39 | ConnectionManager.on("new", this.onNewConnection); 40 | ConnectionManager.on("destroy", this.onDestroyConnection); 41 | }, 42 | 43 | shutdown: function() { 44 | Trace.sysout("WebIDEConnectionsMonitor.shutdown;"); 45 | 46 | }, 47 | 48 | onNewConnection: function(type, connection) { 49 | Trace.sysout("WebIDEConnectionsMonitor.onNewConnection;", arguments); 50 | 51 | Dispatcher.emit("onWebIDEConnectionCreated", [{ connection, selectedRuntime: AppManager.selectedRuntime }]); 52 | 53 | connection.once(Connection.Events.CONNECTED, () => { 54 | if (AppManager.connection == connection) { 55 | 56 | Dispatcher.emit("onWebIDEConnectionReady", [{ connection, selectedRuntime: AppManager.selectedRuntime }]); 57 | } 58 | }); 59 | }, 60 | 61 | onDestroyConnection: function(type, connection) { 62 | Trace.sysout("WebIDEConnectionsMonitor.onDestroyConnection;", arguments); 63 | 64 | Dispatcher.emit("onWebIDEConnectionDestroy", [ { connection } ]); 65 | } 66 | }; 67 | 68 | // Registration 69 | Dispatcher.register(WebIDEConnectionsMonitor); 70 | 71 | // Exports from this module 72 | exports.WebIDEConnectionsMonitor = WebIDEConnectionsMonitor; 73 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Sat May 09 2015 14:49:21 GMT+0000 (UTC) 3 | 4 | /* eslint-env node */ 5 | /* eslint quotes:0 */ 6 | 7 | module.exports = function(config) { 8 | 9 | "use strict"; 10 | 11 | var baseKarmaConfig = { 12 | 13 | // base path that will be used to resolve all patterns (eg. files, exclude) 14 | basePath: '', 15 | 16 | 17 | // frameworks to use 18 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 19 | frameworks: ['jasmine', 'requirejs'], 20 | 21 | 22 | // list of files / patterns to load in the browser 23 | files: [ 24 | 'karma-tests/lib/firebug-sdk-shims.js', 25 | 'karma-tests/test-main.js', 26 | {pattern: 'node_modules/firebug.sdk/lib/reps/**/*.js', included: false }, 27 | {pattern: 'node_modules/firebug.sdk/skin/**/*', included: false}, 28 | {pattern: 'data/**/*.js', included: false}, 29 | {pattern: 'data/**/*.css', included: false}, 30 | {pattern: 'data/**/*.woff', included: false}, 31 | {pattern: 'data/**/*.woff2', included: false}, 32 | {pattern: 'data/**/*.ttf', included: false}, 33 | {pattern: 'data/inspector/res/*', included: false}, 34 | {pattern: 'karma-tests/**/*.js', included: false}, 35 | {pattern: 'karma-tests/**/*spec.js', included: false} 36 | ], 37 | 38 | // list of files to exclude 39 | exclude: [ 40 | ], 41 | 42 | // test results reporter to use 43 | // possible values: 'dots', 'progress' 44 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 45 | reporters: ['progress'], 46 | 47 | // web server port 48 | port: 9876, 49 | 50 | 51 | // enable / disable colors in the output (reporters and logs) 52 | colors: true, 53 | 54 | 55 | // level of logging 56 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 57 | logLevel: config.LOG_INFO, 58 | 59 | // start these browsers 60 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 61 | browsers: ['Firefox'] 62 | }; 63 | 64 | config.set(baseKarmaConfig); 65 | 66 | // optionally enable code coverage 67 | if (process.env.CODE_COVERAGE) { 68 | console.log("CODE COVERAGE ENABLED"); 69 | config.set({ 70 | coverageReporter: { 71 | reporters: [ 72 | { type: "html", subdir: "html/" }, 73 | { type: "text" }, 74 | { type: "text-summary" } 75 | ], 76 | instrumenters: { 77 | istanbul: require("isparta") 78 | } 79 | }, 80 | preprocessors: { 81 | 'data/inspector/**/*.js': ['coverage'] 82 | }, 83 | reporters: baseKarmaConfig.reporters.concat('coverage') 84 | }); 85 | } 86 | }; 87 | -------------------------------------------------------------------------------- /karma-tests/components/main-tabbed-area-spec.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | /* eslint-env jasmine */ 3 | 4 | define(function (require) { 5 | 6 | "use strict"; 7 | 8 | var ReactDOM = require("react-dom"); 9 | 10 | const { ReactLazyFactories } = require("shared/react-helpers"); 11 | const { Provider } = ReactLazyFactories(require("react-redux")); 12 | const { App } = ReactLazyFactories(require("inspector/containers/app")); 13 | 14 | var { PacketsSummaryComponent } = require("inspector/components/packets-summary"); 15 | var { PacketComponent } = require("inspector/components/packet"); 16 | var { PacketsPanelComponent } = require("inspector/components/packets-panel"); 17 | var { ActorsPanelComponent } = require("inspector/components/actors-panel"); 18 | 19 | var { RDPInspectorView } = require("shared/rdp-inspector-window"); 20 | 21 | // actions & store 22 | const store = window.store = require("inspector/store").configureStore(); 23 | const actions = require("inspector/actions/index"); 24 | 25 | const view = new RDPInspectorView({ 26 | window, actions, store, 27 | }); 28 | 29 | var testContainerEl = document.querySelector("#test-container"); 30 | 31 | view.render = function() { 32 | let provider = Provider({ store }, App({ view })); 33 | this.app = ReactDOM.render(provider, testContainerEl); 34 | }; 35 | 36 | view.render(); 37 | 38 | describe("MainTabbedArea React Component", function() { 39 | beforeAll(function () { 40 | jasmine.addMatchers(require("karma-tests/custom-react-matchers")); 41 | }); 42 | 43 | beforeEach(function () { 44 | view.clear(); 45 | }); 46 | 47 | afterAll(function () { 48 | ReactDOM.unmountComponentAtNode(document.querySelector("#test-container")); 49 | }); 50 | 51 | it("renders without errors", function() { 52 | expect(view).toBeDefined(); 53 | 54 | expect(ReactDOM.findDOMNode(view.app)).toEqual(testContainerEl.firstChild); 55 | }); 56 | 57 | it("is composed by a PacketsPanel and an ActorsPanel", function () { 58 | var components = [ 59 | PacketsPanelComponent, 60 | ActorsPanelComponent 61 | ]; 62 | 63 | components.forEach((Component) => { 64 | expect(Component).toBeFoundInReactTree(view.app, 1); 65 | }); 66 | }); 67 | 68 | it("renders a PacketsSummary on PacketsStore.appendSummary", function () { 69 | view.appendSummary(); 70 | 71 | expect(PacketsSummaryComponent).toBeFoundInReactTree(view.app, 1); 72 | }); 73 | 74 | it("renders a Packet on PacketsStore.sendPacket", function () { 75 | var packet = JSON.stringify({ 76 | to: "root", 77 | type: "requestTypes" 78 | }); 79 | 80 | view.onSendPacket({ 81 | data: JSON.stringify({ 82 | packet: packet, 83 | stack: [] 84 | }) 85 | }); 86 | 87 | expect(PacketComponent).toBeFoundInReactTree(view.app, 1); 88 | }); 89 | }); 90 | }); 91 | -------------------------------------------------------------------------------- /data/inspector/components/packet-list.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | define(function(require, exports/*, module*/) { 4 | 5 | "use strict"; 6 | 7 | // Dependencies 8 | const React = require("react"); 9 | const ReactDOM = require("react-dom"); 10 | 11 | // RDP Inspector 12 | const { Packet } = require("./packet"); 13 | const { PacketsSummary } = require("./packets-summary"); 14 | const { PacketsLimit } = require("./packets-limit"); 15 | const { PacketsMessage } = require("./packets-message"); 16 | 17 | // Shortcuts 18 | const { div } = React.DOM; 19 | 20 | /** 21 | * @template This template represents a list of packets displayed 22 | * inside the panel content. 23 | */ 24 | var PacketList = React.createClass({ 25 | /** @lends PacketList */ 26 | 27 | displayName: "PacketList", 28 | 29 | getInitialState: function() { 30 | return { data: [] }; 31 | }, 32 | 33 | componentWillUpdate: function() { 34 | var node = ReactDOM.findDOMNode(this); 35 | this.shouldScrollBottom = node.scrollTop + 36 | node.offsetHeight === node.scrollHeight; 37 | }, 38 | 39 | componentDidUpdate: function() { 40 | if (this.shouldScrollBottom) { 41 | var node = ReactDOM.findDOMNode(this); 42 | node.scrollTop = node.scrollHeight; 43 | } 44 | }, 45 | 46 | render: function() { 47 | var output = []; 48 | var filter = this.props.searchFilter; 49 | var removedPackets = this.props.removedPackets; 50 | 51 | if (removedPackets > 0) { 52 | output.push(PacketsLimit({ 53 | removedPackets: removedPackets 54 | })); 55 | } 56 | 57 | var packets = this.props.data; 58 | for (var i in packets) { 59 | var packet = packets[i]; 60 | 61 | // Render 'summary' packets. 62 | if (packet.type == "summary") { 63 | output.push(PacketsSummary({ 64 | key: packet.id, 65 | data: packet 66 | })); 67 | continue; 68 | } 69 | 70 | // Render 'message' packets. 71 | if (packet.type == "message") { 72 | output.push(PacketsMessage({ 73 | key: packet.id, 74 | data: packet 75 | })); 76 | continue; 77 | } 78 | 79 | // Filter out packets which don't match the search token. 80 | if (filter && packet.rawPacket.toLowerCase().indexOf(filter.toLowerCase()) < 0) { 81 | continue; 82 | } 83 | 84 | var selected = this.props.selectedPacket == packet; 85 | 86 | output.push(Packet({ 87 | key: packet.id, 88 | data: packet, 89 | actions: this.props.actions, 90 | selected: selected, 91 | showInlineDetails: this.props.showInlineDetails 92 | })); 93 | } 94 | 95 | return ( 96 | div({className: "list"}, 97 | div({}, output) 98 | ) 99 | ); 100 | } 101 | }); 102 | 103 | // Exports from this module 104 | exports.PacketList = React.createFactory(PacketList); 105 | exports.PacketListComponent = PacketList; 106 | }); 107 | -------------------------------------------------------------------------------- /data/inspector/components/main-tabbed-area.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | define(function(require, exports, module) { 4 | 5 | "use strict"; 6 | 7 | // ReactJS 8 | var React = require("react"); 9 | var ReactDOM = require("react-dom"); 10 | 11 | // RDP Inspector 12 | const { ActorsPanel } = require("./actors-panel"); 13 | const { PacketsPanel } = require("./packets-panel"); 14 | const { SearchBox } = require("./search-box"); 15 | 16 | // Constants 17 | const { Tabs, Tab, Alert } = require("shared/react-bootstrap-factories"); 18 | 19 | // RDP Window injected APIs 20 | const { Locale } = require("shared/rdp-inspector-window"); 21 | 22 | /** 23 | * @template This template is responsible for rendering the main 24 | * application UI. The UI consist from set of tabs (Packets and Actors) 25 | * displaying list of sent/received packets and list of registered 26 | * actors. 27 | */ 28 | var MainTabbedArea = React.createClass({ 29 | /** @lends MainTabbedArea */ 30 | 31 | displayName: "MainTabbedArea", 32 | 33 | getInitialState: function() { 34 | return { packets: [] }; 35 | }, 36 | 37 | componentDidMount: function() { 38 | var tabbedArea = ReactDOM.findDOMNode(this.refs.tabbedArea); 39 | SearchBox.create(tabbedArea.querySelector(".nav-tabs")); 40 | }, 41 | 42 | componentWillUnmount: function() { 43 | var tabbedArea = ReactDOM.findDOMNode(this.refs.tabbedArea); 44 | SearchBox.destroy(tabbedArea.querySelector(".nav-tabs")); 45 | }, 46 | 47 | onErrorDismiss: function() { 48 | this.props.view.clearError(); 49 | }, 50 | 51 | render: function() { 52 | var packets = Locale.$STR("rdpInspector.tab.Packets"); 53 | var actors = Locale.$STR("rdpInspector.tab.Actors"); 54 | var { error } = this.props.packets; 55 | 56 | return ( 57 | Tabs({className: "mainTabbedArea", defaultActiveKey: 1, 58 | animation: false, ref: "tabbedArea"}, 59 | error ? Alert({ 60 | bsStyle: "warning", 61 | onDismiss: this.onErrorDismiss, 62 | dismissAfter: 2000, 63 | style: { 64 | position: "absolute", 65 | width: "100%", 66 | zIndex: 999999 67 | } 68 | }, error.message) : null, 69 | Tab({ eventKey: 1, title: packets }, 70 | PacketsPanel({ 71 | packets: this.props.packets.filteredPackets, 72 | actions: this.props.view, 73 | selectedPacket: this.props.packets.selectedPacket, 74 | editedPacket: this.props.packets.editedPacket, 75 | searchFilter: this.props.global.searchFilter, 76 | showInlineDetails: this.props.packets.options.showInlineDetails, 77 | packetCacheEnabled: this.props.packets.options.packetCacheEnabled, 78 | removedPackets: this.props.packets.removedPackets, 79 | paused: this.props.packets.paused, 80 | actorIDs: this.props.actors.actorIDs 81 | }) 82 | ), 83 | Tab({ eventKey: 2, title: actors }, 84 | ActorsPanel({ 85 | actions: this.props.view, 86 | actors: this.props.actors, 87 | searchFilter: this.props.global.searchFilter 88 | }) 89 | ) 90 | ) 91 | ); 92 | } 93 | }); 94 | 95 | // Exports from this module 96 | module.exports = MainTabbedArea; 97 | }); 98 | -------------------------------------------------------------------------------- /data/inspector/components/factories.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | define(function(require, exports/*, module*/) { 4 | 5 | "use strict"; 6 | 7 | // Dependencies 8 | var React = require("react"); 9 | var ReactDOM = require("react-dom"); 10 | 11 | // Consts 12 | const { tr, td, table, tbody, thead, th, div, h4 } = React.DOM; 13 | 14 | // Templates 15 | 16 | /** 17 | * TODO docs 18 | */ 19 | var FactoryRow = React.createFactory(React.createClass({ 20 | /** @lends FactoryRow */ 21 | 22 | displayName: "FactoryRow", 23 | 24 | render: function() { 25 | var factory = this.props; 26 | return ( 27 | tr({className: "poolRow"}, 28 | td({}, factory.name), 29 | td({}, factory.prefix), 30 | td({}, factory.ctor) 31 | ) 32 | ); 33 | } 34 | })); 35 | 36 | /** 37 | * TODO docs 38 | * xxxHonza: localization 39 | */ 40 | var FactoryTable = React.createFactory(React.createClass({ 41 | /** @lends FactoryTable */ 42 | 43 | displayName: "FactoryTable", 44 | 45 | render: function() { 46 | var rows = []; 47 | 48 | var factories = this.props.factories; 49 | for (var i in factories) { 50 | if (this.props.searchFilter && 51 | JSON.stringify(factories[i]).toLowerCase() 52 | .indexOf(this.props.searchFilter.toLowerCase()) < 0) { 53 | // filter out packets which don't match the filter 54 | continue; 55 | } 56 | 57 | factories[i].key = factories[i].prefix + factories[i].name; 58 | rows.push(FactoryRow(factories[i])); 59 | } 60 | 61 | return ( 62 | table({className: "poolTable"}, 63 | thead({className: "poolRow"}, 64 | th({width: "33%"}, "Name"), 65 | th({width: "33%"}, "Prefix"), 66 | th({width: "33%"}, "Constructor") 67 | ), 68 | tbody(null, rows) 69 | ) 70 | ); 71 | } 72 | })); 73 | 74 | /** 75 | * TODO docs 76 | */ 77 | var FactoryList = React.createFactory(React.createClass({ 78 | /** @lends FactoryList */ 79 | 80 | displayName: "FactoryList", 81 | 82 | getInitialState: function() { 83 | return { 84 | main: {factories: {}}, 85 | child: {factories: {}}, 86 | searchFilter: null 87 | }; 88 | }, 89 | render: function() { 90 | var main = this.state.main; 91 | var child = this.state.child; 92 | var searchFilter = this.state.searchFilter; 93 | 94 | // xxxHonza: localization 95 | return ( 96 | div({className: "poolContainer"}, 97 | h4(null, "Main Process - Global Factories"), 98 | FactoryTable({ factories: main.factories.global, searchFilter: searchFilter }), 99 | h4(null, "Main Process - Tab Factories"), 100 | FactoryTable({ factories: main.factories.tab, searchFilter: searchFilter }), 101 | h4(null, "Child Process - Global Factories"), 102 | FactoryTable({ factories: child.factories.global, searchFilter: searchFilter }), 103 | h4(null, "Child Process - Tab Factories"), 104 | FactoryTable({ factories: child.factories.tab, searchFilter: searchFilter }) 105 | ) 106 | ); 107 | } 108 | })); 109 | 110 | // TODO: turn this into a more React approach 111 | var Factories = { 112 | render: function(parentNode) { 113 | return ReactDOM.render(FactoryList(), parentNode); 114 | } 115 | }; 116 | 117 | // Exports from this module 118 | exports.Factories = Factories; 119 | 120 | }); 121 | -------------------------------------------------------------------------------- /data/inspector/css/toolbox.css: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | /******************************************************************************/ 4 | /* Main Tabs */ 5 | 6 | .mainTabbedArea { 7 | background-color: rgb(219, 234, 249); 8 | background-image: linear-gradient(rgba(253, 253, 253, 0.2), rgba(253, 253, 253, 0)); 9 | } 10 | 11 | .mainTabbedArea li a { 12 | padding: 3px 8px; 13 | font-weight: bold; 14 | color: #565656; 15 | } 16 | 17 | .mainTabbedArea li.active a, 18 | .mainTabbedArea li.active a:focus { 19 | background-color: rgb(247, 251, 254); 20 | border: 1px solid rgb(170, 188, 207); 21 | border-bottom-color: transparent; 22 | } 23 | 24 | .mainTabbedArea li:hover a { 25 | border: 1px solid #C8C8C8; 26 | border-bottom: 1px solid transparent; 27 | background-color: transparent !important; 28 | } 29 | 30 | .mainTabbedArea li.active:hover a { 31 | border: 1px solid rgb(170, 188, 207) !important; 32 | background-color: rgb(247, 251, 254) !important; 33 | border-bottom-color: transparent !important; 34 | } 35 | 36 | .mainTabbedArea .nav-tabs { 37 | padding-top: 3px; 38 | padding-left: 3px; 39 | height: 30px; 40 | border-bottom-color: rgb(170, 188, 207); 41 | background-color: rgb(219, 234, 249) !important; 42 | background-image: linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.2)); 43 | } 44 | 45 | .tab-content { 46 | /* xxxHonza: minus the height of the tab bar */ 47 | height: calc(100% - 31px); 48 | } 49 | 50 | .tab-pane { 51 | height: 100%; 52 | padding: 0px; 53 | background-color: white; 54 | } 55 | 56 | /******************************************************************************/ 57 | /* SideBar */ 58 | 59 | .sideBarTabbedArea { 60 | height: 30px; 61 | border-bottom: 1px solid rgb(170, 188, 207); 62 | background-color: rgb(219, 234, 249) !important; 63 | background-image: linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.2)); 64 | } 65 | 66 | .sideBarTabbedArea li a { 67 | padding: 4px 9px; 68 | font-weight: normal; 69 | font-size: 12px; 70 | font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; 71 | color: #141414; 72 | } 73 | 74 | .sideBarTabbedArea li.active a, 75 | .sideBarTabbedArea li.active a:focus { 76 | background-color: white; 77 | border: 1px solid rgb(170, 188, 207); 78 | border-bottom-color: transparent; 79 | color: #141414; 80 | } 81 | 82 | .sideBarTabbedArea li:hover a { 83 | border-color: #C8C8C8; 84 | background-color: transparent; 85 | } 86 | 87 | .sideBarTabbedArea li.active:hover a { 88 | border: 1px solid rgb(170, 188, 207) !important; 89 | border-bottom-color: transparent !important; 90 | color: #141414 !important; 91 | } 92 | 93 | .sideBarTabbedArea.nav-tabs { 94 | padding-top: 3px; 95 | padding-left: 3px; 96 | } 97 | 98 | /******************************************************************************/ 99 | /* Panels */ 100 | 101 | .mainPanel { 102 | height: 100%; 103 | } 104 | 105 | .sidePanel { 106 | height: 100%; 107 | } 108 | 109 | .sidePanel > DIV { 110 | height: 100%; 111 | } 112 | 113 | /******************************************************************************/ 114 | /* Actors Panels */ 115 | 116 | .actorsPanelBox { 117 | height: calc(100% - 30px); 118 | } 119 | 120 | .actorsScrollBox { 121 | height: 100%; 122 | padding: 10px; 123 | overflow: auto; 124 | } 125 | -------------------------------------------------------------------------------- /karma-tests/components/packets-toolbar-spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-env jasmine */ 2 | 3 | define(function (require) { 4 | 5 | "use strict"; 6 | 7 | var React = require("react"); 8 | var ReactDOM = require("react-dom"); 9 | var { TestUtils } = React.addons; 10 | 11 | var { PacketsToolbar } = require("inspector/components/packets-toolbar"); 12 | 13 | var packetsToolbar = TestUtils.renderIntoDocument(PacketsToolbar({})); 14 | 15 | var ReactMatchers = require("karma-tests/custom-react-matchers"); 16 | 17 | describe("PacketsToolbar", () => { 18 | beforeAll(() => { 19 | jasmine.addMatchers(ReactMatchers); 20 | }); 21 | 22 | it("renders without errors", () => { 23 | expect(packetsToolbar).toBeDefined(); 24 | 25 | expect(ReactDOM.findDOMNode(packetsToolbar)).toBeDefined(); 26 | }); 27 | 28 | it("contains a 'File' DropdownButton", () => { 29 | expect(packetsToolbar.refs.fileMenu).toBeDefined(); 30 | }); 31 | 32 | describe("File DropdownButton Menu", () => { 33 | // spy actions that should be called 34 | var actions = { 35 | loadPacketsFromFile: jasmine.createSpy("loadPacketsFromFile"), 36 | savePacketsToFile: jasmine.createSpy("savePacketsToFile") 37 | }; 38 | 39 | beforeAll(() => { 40 | packetsToolbar = TestUtils.renderIntoDocument(PacketsToolbar({ 41 | actions: actions 42 | })); 43 | }); 44 | 45 | it("contains a Load and Save MenuItems", () => { 46 | // load and save menuitems should be defined 47 | expect(packetsToolbar.refs.fileLoad).toBeDefined(); 48 | expect(packetsToolbar.refs.fileSave).toBeDefined(); 49 | }); 50 | 51 | it("calls props.actions.loadPacketsFromFile on Load clicks", () => { 52 | var node = ReactDOM.findDOMNode(packetsToolbar.refs.fileLoad); 53 | TestUtils.Simulate.click(node.querySelector("a")); 54 | expect(actions.loadPacketsFromFile).toHaveBeenCalled(); 55 | }); 56 | 57 | it("calls props.actions.savePacketsFromFile on Save clicks", () => { 58 | var node = ReactDOM.findDOMNode(packetsToolbar.refs.fileSave); 59 | TestUtils.Simulate.click(node.querySelector("a")); 60 | expect(actions.savePacketsToFile).toHaveBeenCalled(); 61 | }); 62 | }); 63 | 64 | describe("Options DropdownButton Menu", () => { 65 | // spy actions that should be called 66 | var actions = { 67 | onShowInlineDetails: jasmine.createSpy("onShowInlineDetails"), 68 | onPacketCacheEnabled: jasmine.createSpy("onPacketCacheEnabled") 69 | }; 70 | 71 | beforeAll(() => { 72 | packetsToolbar = TestUtils.renderIntoDocument(PacketsToolbar({ 73 | actions: actions 74 | })); 75 | }); 76 | 77 | it("contains a 'Show Inline Details' and 'Packets Cache' MenuItems", () => { 78 | // load and save menuitems should be defined 79 | expect(packetsToolbar.refs.optionShowInlineDetails).toBeDefined(); 80 | expect(packetsToolbar.refs.optionCachePackets).toBeDefined(); 81 | }); 82 | 83 | it("calls props.actions.onShowInlineDetails on 'Show Inline Details' clicks", () => { 84 | var node = ReactDOM.findDOMNode(packetsToolbar.refs.optionShowInlineDetails); 85 | TestUtils.Simulate.click(node.querySelector("a")); 86 | expect(actions.onShowInlineDetails).toHaveBeenCalled(); 87 | }); 88 | 89 | it("calls props.actions.onPacketCacheEnabled on 'Packet Cache' clicks", () => { 90 | var node = ReactDOM.findDOMNode(packetsToolbar.refs.optionCachePackets); 91 | TestUtils.Simulate.click(node.querySelector("a")); 92 | expect(actions.onPacketCacheEnabled).toHaveBeenCalled(); 93 | }); 94 | }); 95 | }); 96 | 97 | }); 98 | -------------------------------------------------------------------------------- /chrome/skin/classic/shared/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 16 | 18 | 22 | 26 | 27 | 29 | 33 | 37 | 38 | 45 | 48 | 49 | 57 | 65 | 66 | 68 | 69 | 71 | image/svg+xml 72 | 74 | 75 | 76 | 77 | 78 | 82 | 86 | 87 | -------------------------------------------------------------------------------- /data/inspector/components/pools.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | define(function(require, exports/*, module*/) { 4 | 5 | "use strict"; 6 | 7 | // Dependencies 8 | const React = require("react"); 9 | const { tr, td, table, tbody, thead, th, div, h4 } = React.DOM; 10 | 11 | // Templates 12 | 13 | /** 14 | * TODO docs 15 | */ 16 | var PoolRow = React.createFactory(React.createClass({ 17 | /** @lends PoolRow */ 18 | 19 | displayName: "PoolRow", 20 | 21 | render: function() { 22 | var actor = this.props; 23 | return ( 24 | tr({className: "poolRow"}, 25 | td({}, actor.actorID), 26 | td({}, actor.actorPrefix), 27 | td({}, actor.typeName), 28 | td({}, actor.parentID), 29 | td({}, actor.constructor) 30 | ) 31 | ); 32 | } 33 | })); 34 | 35 | /** 36 | * TODO docs 37 | * xxxHonza: localization 38 | */ 39 | var PoolTable = React.createFactory(React.createClass({ 40 | /** @lends PoolTable */ 41 | 42 | displayName: "PoolTable", 43 | 44 | render: function() { 45 | var rows = []; 46 | 47 | // Iterate array of actors. 48 | var actors = this.props.pool; 49 | for (var i in actors) { 50 | if (this.props.searchFilter && 51 | JSON.stringify(actors[i]).toLowerCase() 52 | .indexOf(this.props.searchFilter.toLowerCase()) < 0) { 53 | // filter out packets which don't match the filter 54 | continue; 55 | } 56 | actors[i].key = actors[i].actorID; 57 | rows.push(PoolRow(actors[i])); 58 | } 59 | 60 | // Pools are mixed with Actor objects (created using CreateClass). 61 | var className = "poolTable"; 62 | if (this.props.actorClass) { 63 | className += " actorClass"; 64 | } 65 | 66 | var id = this.props.id ? "ID: " + this.props.id : ""; 67 | 68 | return ( 69 | div({}, 70 | h4({}, "Pool" + id), 71 | table({className: className}, 72 | thead({className: "poolRow"}, 73 | th({width: "20%"}, "Actor ID"), 74 | th({width: "20%"}, "Prefix"), 75 | th({width: "20%"}, "TypeName"), 76 | th({width: "20%"}, "Parent"), 77 | th({width: "20%"}, "Constructor") 78 | ), 79 | tbody(null, rows) 80 | ) 81 | ) 82 | ); 83 | } 84 | })); 85 | 86 | /** 87 | * TODO docs 88 | */ 89 | var PoolList = React.createFactory(React.createClass({ 90 | /** @lends PoolList */ 91 | 92 | displayName: "PoolList", 93 | 94 | getInitialState: function() { 95 | return { 96 | pools: [] 97 | }; 98 | }, 99 | 100 | render: function() { 101 | var pools = []; 102 | 103 | for (var i in this.state.pools) { 104 | var poolData = this.state.pools[i]; 105 | var pool = poolData.pool; 106 | var poolId = poolData.id; 107 | 108 | var actorClass = false; 109 | 110 | // xxxHonza: there are actors stored as pools. 111 | // See also: https://bugzilla.mozilla.org/show_bug.cgi?id=1119790#c1 112 | if (!Array.isArray(pool)) { 113 | pool = [pool]; 114 | actorClass = true; 115 | } 116 | 117 | pools.push(PoolTable({ 118 | pool: pool, 119 | actorClass: actorClass, 120 | id: poolId, 121 | key: i, 122 | searchFilter: this.state.searchFilter 123 | })); 124 | } 125 | 126 | return ( 127 | div({className: "poolContainer"}, 128 | pools 129 | ) 130 | ); 131 | } 132 | })); 133 | 134 | var Pools = { 135 | render: function(parentNode) { 136 | return React.render(PoolList(), parentNode); 137 | } 138 | }; 139 | 140 | // Exports from this module 141 | exports.Pools = Pools; 142 | 143 | }); 144 | -------------------------------------------------------------------------------- /chrome/locale/en-US/toolbox.properties: -------------------------------------------------------------------------------- 1 | # LOCALIZATION NOTE (rdpInspector.title): RDP Inspector extension title 2 | rdpInspector.title=RDP Inspector 3 | 4 | # LOCALIZATION NOTE (rdpInspector.button.title, rdpInspector.button.tip): 5 | # Label and a tooltip for RDP Inspector start button available in the 6 | # Firefox toolbar and Style Editor panel. This button opens RDP Inspector 7 | # window. 8 | rdpInspector.startButton.title=RDP Inspector 9 | rdpInspector.startButton.tip=Remote debugging protocol inspector. 10 | 11 | # LOCALIZATION NOTE (rdpInspector.menu.about.label, rdpInspector.menu.about.tip): 12 | # RDP Inspector menu label. The menu is available in RDP Inspector start 13 | # button located in Firefox toolbar. 14 | rdpInspector.menu.about.label=About RDP Inspector... 15 | rdpInspector.menu.about.tip=Information about RDP Inspector 16 | 17 | # LOCALIZATION NOTE (rdpInspector.menu.visitHomePage.label, rdpInspector.menu.visitHomePage.tip): 18 | # RDP Inspector menu label. The menu is available in RDP Inspector start 19 | # button located in Firefox toolbar. 20 | rdpInspector.menu.visitHomePage.label=Visit Home Page... 21 | rdpInspector.menu.visitHomePage.tip=Learn more about RDP Inspector 22 | 23 | # LOCALIZATION NOTE (rdpInspector.menu.reportIssue.label, rdpInspector.menu.reportIssue.tip): 24 | # RDP Inspector menu label. The menu is available in RDP Inspector start 25 | # button located in Firefox toolbar. 26 | rdpInspector.menu.reportIssue.label=Report Issue... 27 | rdpInspector.menu.reportIssue.tip=Did you find a bug or do you need a new feature? Let us know! 28 | 29 | # LOCALIZATION NOTE (rdpInspector.menu.group.label, rdpInspector.menu.group.tip): 30 | # RDP Inspector menu label. The menu is available in RDP Inspector start 31 | # button located in Firefox toolbar. 32 | rdpInspector.menu.group.label=Discussion Group... 33 | rdpInspector.menu.group.tip=Open the discussion group site (shared with Firebug) 34 | 35 | # LOCALIZATION NOTE (rdpInspector.menu.options.label): 36 | # RDP Inspector menu label. The menu is available in RDP Inspector start button locate in Firefox toolbar. 37 | rdpInspector.menu.options.label=Options 38 | 39 | # LOCALIZATION NOTE (rdpInspector.menu.packetCache.label,rdpInspector.menu.packetCache.tip): 40 | # RDP Inspector menu label. The menu is available in RDP Inspector start 41 | # button located in Firefox toolbar, inside the Options sub-menu. 42 | rdpInspector.menu.packetCache.label=Packet Cache 43 | rdpInspector.menu.packetCache.tip=Store packets in a cache before the console is opened. 44 | 45 | # LOCALIZATION NOTE (rdpInspector.menu.showInlineDetails.label,rdpInspector.menu.showInlineDetails.tip): 46 | # RDP Inspector menu label. The menu is available in RDP Inspector start 47 | # button located in Firefox toolbar, inside the Options sub-menu. 48 | rdpInspector.menu.showInlineDetails.label=Show Packet Details Inline 49 | rdpInspector.menu.showInlineDetails.tip=Show a detailed tree view of the Packet content. 50 | 51 | # LOCALIZATION NOTE (rdpInspector.menu.autoOpenOnWebIDEConnection.label,rdpInspector.menu.autoOpenOnWebIDEConnection.tip): 52 | # RDP Inspector menu label. The menu is available in RDP Inspector start 53 | # button located in Firefox toolbar, inside the Options sub-menu. 54 | rdpInspector.menu.autoOpenOnWebIDEConnection.label=Open automatically on new WebIDE connections 55 | rdpInspector.menu.autoOpenOnWebIDEConnection.tip=Open an RDP inspector window automatically on every new WebIDE connections. 56 | 57 | # LOCALIZATION NOTE (rdpInspector.menu.connectionList.label, rdpInspector.menu.connectionList.tip): 58 | # RDP Inspector menu label. The menu is available in RDP Inspector start 59 | # button located in Firefox toolbar. 60 | rdpInspector.menu.connectionList.label=Connect to... 61 | rdpInspector.menu.connectionList.tip=Open a dialog which lists all the RDP Connections available to the RDPInspector 62 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rdpinspector", 3 | "title": "RDP Inspector", 4 | "id": "rdpinspector@getfirebug.com", 5 | "description": "Remote debugger protocol inspector", 6 | "author": "Firebug Working Group", 7 | "icon": "chrome://rdpinspector/skin/icon32.png", 8 | "homepage": "https://github.com/firebug/rdp-inspector/wiki", 9 | "forum": "https://groups.google.com/forum/#!forum/firebug", 10 | "contributors": [ 11 | "Jan Odvarko (Mozilla Corp.)", 12 | "Luca Greco", 13 | "Sebastian Zartner", 14 | "Jarda Snajdr" 15 | ], 16 | "translators": [ 17 | "YFdyh000 (zh-CN)", 18 | "Markh van BabelZilla.org (nl)", 19 | "Marco Aurelio Krause (pt-BR)" 20 | ], 21 | "license": "BSD-3-Clause", 22 | "version": "1.2.5", 23 | "scripts": { 24 | "test": "npm run karma-tests && npm run jpm-tests", 25 | "karma-tests": "karma start --single-run --reporters spec", 26 | "karma-coverage": "CODE_COVERAGE=true npm run karma-tests -- --reporters spec,coverage", 27 | "watch-karma-tests": "karma start --no-single-run --reporters spec --auto-watch", 28 | "watch-karma-coverage": "CODE_COVERAGE=true npm run watch-karma-tests", 29 | "jpm-tests": "jpm test", 30 | "travis-ci": "npm run karma-coverage && npm run lint", 31 | "lint-content": "eslint data/inspector data/connection-list/ data/shared/ && eslint karma-tests/", 32 | "lint-addon": "eslint lib", 33 | "lint": "npm run lint-content && npm run lint-addon" 34 | }, 35 | "engines": { 36 | "firefox": ">=38.0" 37 | }, 38 | "main": "index.js", 39 | "repository": { 40 | "type": "git", 41 | "url": "https://github.com/firebug/rdp-inspector.git" 42 | }, 43 | "bugs": { 44 | "url": "https://github.com/firebug/rdp-inspector/issues" 45 | }, 46 | "preferences-branch": "rdpinspector", 47 | "preferences": [ 48 | { 49 | "name": "autoOpenOnWebIDEConnection", 50 | "title": "Open RDP Inspector window automatically on new WebIDE connections", 51 | "description": "auto-open an RDP inspector window on every new WebIDE connections", 52 | "type": "bool", 53 | "value": false 54 | }, 55 | { 56 | "name": "showInlineDetails", 57 | "title": "Show Packet Details Inline", 58 | "description": "Show a detailed tree view of the Packet content", 59 | "type": "bool", 60 | "value": false 61 | }, 62 | { 63 | "name": "packetLimit", 64 | "title": "Maximum number of displayed packets", 65 | "description": "Maximum number of packets displayed in the RDP Inspector console", 66 | "type": "integer", 67 | "value": 500 68 | }, 69 | { 70 | "name": "packetCacheEnabled", 71 | "title": "Enable packets caching", 72 | "type": "bool", 73 | "value": false 74 | }, 75 | { 76 | "name": "packetCacheSize", 77 | "title": "Maximum number of packets in the cache", 78 | "description": "Maximum number of packets stored in a cache that's holding packets before the console is opened.", 79 | "type": "integer", 80 | "value": 200 81 | } 82 | ], 83 | "dependencies": { 84 | "firebug.sdk": "~0.6.7" 85 | }, 86 | "devDependencies": { 87 | "eslint": "^0.21.0", 88 | "eslint-plugin-no-tabs": "git://github.com/rpl/eslint-plugin-no-tabs", 89 | "eslint-plugin-react": "git://github.com/rpl/eslint-plugin-react#fix/displayName-and-propTypes-on-jsx-false", 90 | "isparta": "^3.0.3", 91 | "jasmine-core": "^2.3.2", 92 | "karma": "^0.12.31", 93 | "karma-cli": "0.0.4", 94 | "karma-coverage": "^0.3.1", 95 | "karma-firefox-launcher": "^0.1.4", 96 | "karma-jasmine": "^0.3.5", 97 | "karma-requirejs": "^0.2.2", 98 | "karma-spec-reporter": "0.0.19", 99 | "requirejs": "^2.1.17" 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /lib/transport-observer.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | "use strict"; 4 | 5 | module.metadata = { 6 | "stability": "experimental" 7 | }; 8 | 9 | // Add-on SDK 10 | const { Class } = require("sdk/core/heritage"); 11 | const { EventTarget } = require("sdk/event/target"); 12 | 13 | // Firebug SDK 14 | const { Trace/*, TraceError*/ } = require("firebug.sdk/lib/core/trace.js").get(module.id); 15 | const { Arr } = require("firebug.sdk/lib/core/array.js"); 16 | //const { Options } = require("firebug.sdk/lib/core/options.js"); 17 | 18 | /** 19 | * This object registers a listener in transport layer that is associated 20 | * with given client object {@DebuggerClient}. All sent and received 21 | * packets are consequently forwarded to other listeners. 22 | * One of the listeners is the {@ToolboxOverlay} object that implements 23 | * packet caching (and also forwards packets to RDP Window if it exists). 24 | */ 25 | const TransportObserver = Class( 26 | /** @lends TransportObserver */ 27 | { 28 | extends: EventTarget, 29 | 30 | // Initialization 31 | 32 | initialize: function(options) { 33 | Trace.sysout("TransportObserver.initialize;", options); 34 | 35 | this.listeners = []; 36 | 37 | this.client = options.client; 38 | 39 | this.send = this.send.bind(this); 40 | this.onPacket = this.onPacket.bind(this); 41 | this.onBulkPacket = this.onBulkPacket.bind(this); 42 | this.startBulkSend = this.startBulkSend.bind(this); 43 | this.onClosed = this.onClosed.bind(this); 44 | 45 | let transport = this.client._transport; 46 | transport.on("send", this.send); 47 | transport.on("onPacket", this.onPacket); 48 | transport.on("onBulkPacket", this.onBulkPacket); 49 | transport.on("startBulkSend", this.startBulkSend); 50 | transport.on("onClosed", this.onClosed); 51 | 52 | transport.on("packet", this.onPacket); 53 | transport.on("bulkpacket", this.onBulkPacket); 54 | transport.on("startbulksend", this.startBulkSend); 55 | transport.on("close", this.onClosed); 56 | }, 57 | 58 | destroy: function() { 59 | Trace.sysout("TransportObserver.destroy;"); 60 | 61 | let transport = this.client._transport; 62 | transport.off("send", this.send); 63 | transport.off("onPacket", this.onPacket); 64 | transport.off("onBulkPacket", this.onBulkPacket); 65 | transport.off("startBulkSend", this.startBulkSend); 66 | transport.off("onClosed", this.onClosed); 67 | 68 | transport.off("packet", this.onPacket); 69 | transport.off("bulkpacket", this.onBulkPacket); 70 | transport.off("startbulksend", this.startBulkSend); 71 | transport.off("close", this.onClosed); 72 | }, 73 | 74 | // Connection Events 75 | 76 | send: function(eventId, packet) { 77 | Trace.sysout("PACKET SEND " + JSON.stringify(packet), packet); 78 | 79 | packet = JSON.parse(JSON.stringify(packet)); 80 | 81 | this.listeners.forEach(listener => { 82 | listener.onSendPacket(packet); 83 | }); 84 | }, 85 | 86 | onPacket: function(eventId, packet) { 87 | Trace.sysout("PACKET RECEIVED; " + JSON.stringify(packet), packet); 88 | 89 | packet = JSON.parse(JSON.stringify(packet)); 90 | 91 | this.listeners.forEach(listener => { 92 | listener.onReceivePacket(packet); 93 | }); 94 | }, 95 | 96 | startBulkSend: function(/*eventId, header*/) { 97 | }, 98 | 99 | onBulkPacket: function(/*eventId, packet*/) { 100 | }, 101 | 102 | onClosed: function(/*eventId, status*/) { 103 | }, 104 | 105 | // Listeners 106 | 107 | addListener: function(listener) { 108 | this.listeners.push(listener); 109 | }, 110 | 111 | removeListener: function(listener) { 112 | Arr.remove(this.listeners, listener); 113 | } 114 | }); 115 | 116 | // Exports from this module 117 | exports.TransportObserver = TransportObserver; 118 | -------------------------------------------------------------------------------- /chrome/locale/en-US/inspector.properties: -------------------------------------------------------------------------------- 1 | # LOCALIZATION NOTE (rdpInspector.tab.Packets, rdpInspector.tab.Actors) 2 | # A label for main application tabs. 3 | rdpInspector.tab.Packets=Packets 4 | rdpInspector.tab.Actors=Actors 5 | 6 | # LOCALIZATION NOTE (rdpInspector.option.showInlineDetails, 7 | # rdpInspector.option.showInlineDetails): Label and 8 | # tooltip for an option that can be set within the Packets tab. If set to true 9 | # every packet display inline preview of packet properties (collapsed 10 | # by default) 11 | rdpInspector.option.showInlineDetails=Show Packet Details Inline 12 | rdpInspector.option.showInlineDetails.tip=Show a detailed tree view of the Packet content. 13 | 14 | # LOCALIZATION NOTE (rdpInspector.option.packetCacheEnabled, rdpInspector.options.packetCacheEnabled.tip): Label and 15 | # tooltip for an option that can be set within the Packets tab. If set to true 16 | # the inspector caches packets that are sent/received before the 17 | # inspector window is opened (to see also packets that happens 18 | # at the very beginning) 19 | rdpInspector.option.packetCacheEnabled=Packet Cache 20 | rdpInspector.option.packetCacheEnabled.tip=Store packets in a cache before the console is opened. 21 | 22 | # LOCALIZATION NOTE (rdpInspector.cmd.clear): Label for a toolbar button 23 | # in the Packets tab. This command clears the packet list. 24 | rdpInspector.cmd.clear=Clear 25 | 26 | # LOCALIZATION NOTE (rdpInspector.cmd.find): Label for a toolbar button 27 | # in the Packets tab. This command opens standard Find bar at the bottom 28 | # of the Inspector window. 29 | rdpInspector.cmd.find=Find 30 | 31 | # LOCALIZATION NOTE (rdpInspector.cmd.summary): Label for a toolbar button 32 | # in the Packets tab. This command inserts a summary separator in the 33 | # packets list. 34 | rdpInspector.cmd.summary=Summary 35 | 36 | # LOCALIZATION NOTE (rdpInspector.tooltip.sizeSent, rdpInspector.tooltip.sizeReceived, 37 | # rdpInspector.tooltip.packetsSent, rdpInspector.tooltip.packetsReceived): 38 | # A tooltip for summary info in the packet list. 39 | rdpInspector.tooltip.sizeSent=Total amount of data sent to the debugger server 40 | rdpInspector.tooltip.sizeReceived=Total amount of data received from the debugger server 41 | rdpInspector.tooltip.packetsSent=Number of packets sent to the debugger server 42 | rdpInspector.tooltip.packetsReceived=Number of packets received from the debugger server 43 | 44 | # LOCALIZATION NOTE (rdpInspector.label.packets, rdpInspector.label.data) 45 | # A label for summary info in the packet list. 46 | rdpInspector.label.packets=Packets 47 | rdpInspector.label.data=Data 48 | 49 | # LOCALIZATION NOTE (rdpInspector.cmd.refresh) A label used on Actor's 50 | # tab toolbar. It's used to refresh the tab's content. 51 | rdpInspector.cmd.refresh=Refresh 52 | 53 | # LOCALIZATION NOTE (rdpInspector.label.sent, rdpInspector.label.to, 54 | # rdpInspector.label.received, rdpInspector.label.from): A label for 55 | # packets displayed in the packet list. 56 | rdpInspector.label.sent=Sent 57 | rdpInspector.label.to=to 58 | rdpInspector.label.received=Received 59 | rdpInspector.label.from=from 60 | 61 | # LOCALIZATION NOTE (rdpInspector.label.outOfLimit) A label displayed 62 | # at the top of the packet list in case the number of displayed packets 63 | # reached maximum limit. 64 | rdpInspector.label.outOfLimit=packets removed 65 | 66 | # LOCALIZATION NOTE (rdpInspector.label.Paused, rdpInspector.label.Unpaused): 67 | # A label used for pause/unpause packet collecting. The label is for a message 68 | # that is printed into the packet list when the user click the Pause button 69 | # in the toolbar. 70 | rdpInspector.label.Paused=Paused 71 | rdpInspector.label.Unpaused=Unpaused 72 | 73 | # LOCALIZATION NOTE (rdpInspector.label.MainProcess, rdpInspector.label.ChildProcess, 74 | # rdpInspector.ActorsFactories): 75 | # Labels used for the actors sub-panel selector in the actors panel toolbar. 76 | rdpInspector.label.MainProcess=Main Process 77 | rdpInspector.label.ChildProcess=Child Process 78 | rdpInspector.label.ActorsFactories=Actors Factories 79 | 80 | # LOCALIZATION NOTE (rdpInspector.menu.File, rdpInspector.menu.Options, 81 | # rdpInspector.cmd.load, rdpInspector.cmd.save): 82 | # Labels for the dropdown menus in the packets panel toolbar. 83 | rdpInspector.menu.File=File 84 | rdpInspector.menu.Options=Options 85 | rdpInspector.cmd.load=Load 86 | rdpInspector.cmd.save=Save 87 | -------------------------------------------------------------------------------- /karma-tests/components/packet-spec.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | /* eslint-env jasmine */ 3 | 4 | define(function (require) { 5 | 6 | "use strict"; 7 | 8 | var React = require("react"); 9 | var ReactDOM = require("react-dom"); 10 | var { TestUtils } = React.addons; 11 | 12 | var { Packet } = require("inspector/components/packet"); 13 | const { TreeViewComponent } = require("reps/tree-view"); 14 | 15 | var ReactMatchers = require("karma-tests/custom-react-matchers"); 16 | 17 | describe("Packet", () => { 18 | beforeAll(() => { 19 | jasmine.addMatchers(ReactMatchers); 20 | }); 21 | 22 | //TODO: currently skipped, until TreeView component is exported from firebug.sdk 23 | xit("contains a TreeView for props.data.packet only if props.showInlineDetails is true", () => { 24 | var packet; 25 | 26 | var data = { 27 | type: "receive", 28 | size: 0, 29 | id: 1, 30 | time: new Date("2015-06-09T16:48:50.162Z"), 31 | packet: {} 32 | }; 33 | 34 | packet = TestUtils.renderIntoDocument(Packet({ 35 | showInlineDetails: false, 36 | data: data 37 | })); 38 | 39 | //TODO: needs TreeView component exported from firebug.sdk 40 | expect(TreeViewComponent).toBeFoundInReactTree(packet, 0); 41 | 42 | packet = TestUtils.renderIntoDocument(Packet({ 43 | showInlineDetails: true, 44 | data: data 45 | })); 46 | 47 | //TODO: needs TreeView component exported from firebug.sdk 48 | expect(TreeViewComponent).toBeFoundInReactTree(packet, 1); 49 | }); 50 | 51 | describe("context menu actions", () => { 52 | it("has 'Edit and Resent' action in the sent packet's context menu", () => { 53 | var packetData = { 54 | "type": "fakeSentPacket", 55 | "to": "server1.conn1.child1/fakeActor2" 56 | }; 57 | var data = { 58 | type: "send", 59 | id: 1, 60 | time: new Date("2015-06-09T16:48:50.162Z"), 61 | packet: packetData, 62 | size: JSON.stringify(packetData).length 63 | }; 64 | 65 | var actions = { 66 | editPacket: jasmine.createSpy("editPacket"), 67 | selectPacket: jasmine.createSpy("selectPacket") 68 | }; 69 | 70 | var packet = TestUtils.renderIntoDocument(Packet({ 71 | showInlineDetails: false, 72 | data: data, 73 | actions: actions 74 | })); 75 | 76 | expect(packet.refs.contextMenu).not.toBeDefined(); 77 | 78 | var el = ReactDOM.findDOMNode(packet); 79 | TestUtils.Simulate.contextMenu(el); 80 | 81 | // right clicking a packet opens a context menu and 82 | // select the packet 83 | expect(actions.selectPacket).toHaveBeenCalled(); 84 | expect(packet.refs.contextMenu).toBeDefined(); 85 | expect(packet.refs.editAndResendAction).toBeDefined(); 86 | 87 | // clicking on the 'Edit and Resend' action 88 | var actionEl = ReactDOM.findDOMNode(packet.refs.editAndResendAction); 89 | TestUtils.Simulate.click(actionEl); 90 | expect(actions.editPacket).toHaveBeenCalled(); 91 | }); 92 | }); 93 | 94 | describe("issues", () => { 95 | it("#44 - Long packet value breaks the view", () => { 96 | var data = { 97 | type: "receive", 98 | size: 0, 99 | id: 1, 100 | time: new Date("2015-06-09T16:48:50.162Z"), 101 | packet: { 102 | error: null, 103 | message: { 104 | "groupName": "", 105 | "columnNumber": 13, 106 | "lineNumber": 48, 107 | "workerType": "none", 108 | "level": "table", 109 | "counter": null, 110 | "arguments": [], 111 | "functionName": "onExecuteTest1" 112 | }, 113 | "type": "consoleAPICall", 114 | "from": "server1.conn1.child1/consoleActor2" 115 | } 116 | }; 117 | 118 | var packet = TestUtils.renderIntoDocument(Packet({ 119 | showInlineDetails: false, 120 | data: data 121 | })); 122 | 123 | var el = ReactDOM.findDOMNode(packet); 124 | 125 | expect(el).toBeDefined(); 126 | 127 | // NOTE: prior the fix, a long "consoleAPICall" received packet 128 | // was wrongly turned into a "div.errorMessage" 129 | var errorMessage = el.querySelector(".errorMessage"); 130 | expect(errorMessage).toEqual(null); 131 | }); 132 | }); 133 | 134 | }); 135 | 136 | }); 137 | -------------------------------------------------------------------------------- /data/inspector/components/packets-toolbar.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | define(function(require, exports/*, module*/) { 4 | 5 | "use strict"; 6 | 7 | // ReactJS 8 | var React = require("react"); 9 | 10 | // Constants 11 | const { 12 | ButtonToolbar, Button, 13 | DropdownButton, MenuItem, 14 | OverlayTrigger, Tooltip 15 | } = require("shared/react-bootstrap-factories"); 16 | 17 | const { span, input } = React.DOM; 18 | 19 | // RDP Window injected APIs 20 | const { Locale } = require("shared/rdp-inspector-window"); 21 | 22 | /** 23 | * @template This object represents a template for a toolbar displayed 24 | * within the Packets tab 25 | */ 26 | var PacketsToolbar = React.createClass({ 27 | /** @lends PacketsToolbar */ 28 | 29 | displayName: "PacketsToolbar", 30 | 31 | render: function() { 32 | var { showInlineDetails, packetCacheEnabled } = this.props; 33 | 34 | var labels = {}; 35 | labels.inlineDetails = Locale.$STR("rdpInspector.option.showInlineDetails"); 36 | labels.cachePackets = Locale.$STR("rdpInspector.option.packetCacheEnabled"); 37 | 38 | var tooltips = {}; 39 | tooltips.inlineDetails = Locale.$STR("rdpInspector.option.showInlineDetails.tip"); 40 | tooltips.cachePackets = Locale.$STR("rdpInspector.option.packetCacheEnabled.tip"); 41 | 42 | var paused = this.props.paused; 43 | var pauseClassName = paused ? "btn-warning" : ""; 44 | 45 | return ( 46 | ButtonToolbar({className: "toolbar"}, 47 | DropdownButton({ bsSize: "xsmall", ref: "fileMenu", title: Locale.$STR("rdpInspector.menu.File")}, 48 | MenuItem({key: "fileLoad", ref: "fileLoad", onClick: this.onLoadPacketsFromFile }, 49 | Locale.$STR("rdpInspector.cmd.load")), 50 | MenuItem({key: "fileSave", ref: "fileSave", onClick: this.onSavePacketsFromFile }, 51 | Locale.$STR("rdpInspector.cmd.save")) 52 | ), 53 | DropdownButton({ 54 | bsSize: "xsmall", title: Locale.$STR("rdpInspector.menu.Options"), 55 | className: "pull-right", ref: "options" 56 | }, 57 | OverlayTrigger({placement: "bottom", overlay: Tooltip({}, tooltips.inlineDetails)}, 58 | MenuItem({key: "inlineDetails", ref: "optionShowInlineDetails", onClick: this.onShowInlineDetails}, 59 | input({type: "checkbox", checked: showInlineDetails}), labels.inlineDetails 60 | ) 61 | ), 62 | OverlayTrigger({placement: "bottom", overlay: Tooltip({}, tooltips.cachePackets)}, 63 | MenuItem({key: "cachePackets", ref: "optionCachePackets", onClick: this.onCachePackets}, 64 | input({type: "checkbox", checked: packetCacheEnabled}), labels.cachePackets 65 | ) 66 | ) 67 | ), 68 | Button({bsSize: "xsmall", onClick: this.onClear}, 69 | Locale.$STR("rdpInspector.cmd.clear") 70 | ), 71 | Button({bsSize: "xsmall", onClick: this.onFind}, 72 | Locale.$STR("rdpInspector.cmd.find") 73 | ), 74 | Button({bsSize: "xsmall", onClick: this.onSummary}, 75 | Locale.$STR("rdpInspector.cmd.summary") 76 | ), 77 | Button({bsSize: "xsmall", className: pauseClassName, 78 | onClick: this.onPause}, 79 | span({className: "glyphicon glyphicon-pause", "aria-hidden": true}) 80 | ) 81 | ) 82 | ); 83 | }, 84 | 85 | // Commands 86 | 87 | onClear: function(/*event*/) { 88 | this.props.actions.clear(); 89 | }, 90 | 91 | onFind: function(/*event*/) { 92 | this.props.actions.find(); 93 | }, 94 | 95 | onSummary: function(/*event*/) { 96 | this.props.actions.appendSummary(); 97 | }, 98 | 99 | onShowInlineDetails: function() { 100 | this.props.actions.onShowInlineDetails(); 101 | }, 102 | 103 | onCachePackets: function() { 104 | this.props.actions.onPacketCacheEnabled(); 105 | }, 106 | 107 | onPause: function(/*event*/) { 108 | this.props.actions.onPause(); 109 | }, 110 | 111 | onLoadPacketsFromFile: function(/*event*/) { 112 | this.props.actions.loadPacketsFromFile(); 113 | this.refs.fileMenu.setState({ open: false }); 114 | 115 | }, 116 | 117 | onSavePacketsFromFile: function(/*event*/) { 118 | this.props.actions.savePacketsToFile(); 119 | this.refs.fileMenu.setState({ open: false }); 120 | } 121 | }); 122 | 123 | // Exports from this module 124 | exports.PacketsToolbar = React.createFactory(PacketsToolbar); 125 | exports.PacketsToolbarComponent = PacketsToolbar; 126 | }); 127 | -------------------------------------------------------------------------------- /data/inspector/components/packet-editor.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | define(function(require, exports/*, module*/) { 4 | 5 | "use strict"; 6 | 7 | // Dependencies 8 | const React = require("react"); 9 | 10 | // Firebug SDK 11 | const { TreeEditorView } = require("./tree-editor-view"); 12 | 13 | // Constants 14 | const { div } = React.DOM; 15 | 16 | const { ButtonToolbar, Button } = require("shared/react-bootstrap-factories"); 17 | 18 | /** 19 | * @template This template represents the Packet Editor toolbar 20 | */ 21 | 22 | var PacketEditorToolbar = React.createFactory(React.createClass({ 23 | /** @lends PacketEditrView */ 24 | 25 | displayName: "PacketEditorToolbar", 26 | 27 | render: function() { 28 | return ButtonToolbar({className: "toolbar"}, [ 29 | Button({ onClick: this.props.onSend, key: "send", 30 | bsStyle: "primary", bsSize: "xsmall", 31 | style: { marginLeft: 12, color: "white" } }, "Send"), 32 | 33 | Button({ onClick: this.props.onRedo, key: "redo", 34 | className: "pull-right", 35 | disabled: !this.props.isRedoEnabled, 36 | bsStyle: "default", bsSize: "xsmall", 37 | style: { marginRight: 6 } }, "Redo"), 38 | Button({ onClick: this.props.onUndo, key: "undo", 39 | className: "pull-right", 40 | disabled: !this.props.isUndoEnabled, 41 | bsStyle: "default", bsSize: "xsmall", 42 | style: { marginRight: 6 } }, "Undo"), 43 | Button({ onClick: this.props.onClear, key: "clear", 44 | className: "pull-right", 45 | bsStyle: "danger", bsSize: "xsmall", 46 | style: { marginRight: 6, color: "white" } }, "Clear") 47 | ]); 48 | } 49 | })); 50 | 51 | /** 52 | * @template This template represents the PacketEditor sidebar 53 | */ 54 | var PacketEditor = React.createClass({ 55 | displayName: "PacketEditor", 56 | 57 | getInitialState: function() { 58 | return { 59 | editedPacket: null, 60 | defaultData: { 61 | to: "root", 62 | type: "requestTypes" 63 | } 64 | }; 65 | }, 66 | 67 | componentWillReceiveProps: function(nextProps) { 68 | this.setState({ 69 | editedPacket: nextProps.editedPacket 70 | }); 71 | }, 72 | 73 | render: function() { 74 | var editedPacket = this.state.editedPacket || this.props.editedPacket; 75 | var { actorIDs } = this.props; 76 | 77 | return ( 78 | div({className: "details editor"}, 79 | PacketEditorToolbar({ 80 | onClear: this.onClear, 81 | onSend: this.onSend, 82 | onUndo: this.onUndo, 83 | onRedo: this.onRedo, 84 | isUndoEnabled: true, 85 | isRedoEnabled: true 86 | }), 87 | TreeEditorView({ 88 | ref: "editor", 89 | key: "packet-editor", 90 | data: editedPacket, 91 | defaultData: this.state.defaultData, 92 | handleAutocompletion: (suggestionType, keyPath, value) => { 93 | if (!actorIDs && suggestionType != "value") { 94 | return []; 95 | } 96 | 97 | try { 98 | var parsedValue; 99 | 100 | // remove starting and ending '"' if any 101 | parsedValue = typeof value == "string" ? 102 | value.replace(/^"|"$/g, "") : ""; 103 | 104 | if(keyPath.length == 1 && keyPath[0] == "to") { 105 | // get a suggestion list by filtering actorIDs list 106 | return actorIDs.filter((suggestion) => { 107 | return suggestion.indexOf(parsedValue) >= 0; 108 | }); 109 | } 110 | } catch(e) { 111 | //console.debug("exception on parsing autocompletion", e); 112 | } 113 | 114 | return []; 115 | } 116 | }) 117 | ) 118 | ); 119 | }, 120 | 121 | onUndo: function() { 122 | this.refs.editor.undo(); 123 | }, 124 | 125 | onRedo: function() { 126 | this.refs.editor.redo(); 127 | }, 128 | 129 | onClear: function() { 130 | this.refs.editor.clearData(); 131 | }, 132 | 133 | onSend: function() { 134 | this.props.actions.send(this.refs.editor.getData()); 135 | } 136 | }); 137 | 138 | // Exports from this module 139 | exports.PacketEditor = React.createFactory(PacketEditor); 140 | 141 | }); 142 | -------------------------------------------------------------------------------- /data/inspector/components/stack-frame-rep.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | define(function(require, exports /*, module */) { 4 | 5 | "use strict"; 6 | 7 | // ReactJS 8 | const React = require("react"); 9 | 10 | // Firebug SDK 11 | const { Reps } = require("reps/reps"); 12 | const { ObjectBox } = require("reps/object-box"); 13 | 14 | // Constants 15 | const { span } = React.DOM; 16 | 17 | // RDP Window injected APIs 18 | const { postChromeMessage } = require("shared/rdp-inspector-window"); 19 | 20 | /** 21 | * This component is responsible for rendering a stack frame. 22 | */ 23 | var StackFrameRep = React.createClass({ 24 | /** @lends StackFrameRep */ 25 | 26 | displayName: "StackFrameRep", 27 | 28 | render: function() { 29 | var stackFrame = this.props.object; 30 | var name = stackFrame.name; 31 | var label = stackFrame.getLabel(); 32 | 33 | return ( 34 | ObjectBox({className: "stackFrame"}, 35 | span({className: "stackName"}, name + "()"), 36 | span({className: "stackLabel", onClick: this.onViewSource}, label) 37 | ) 38 | ); 39 | }, 40 | 41 | onViewSource: function(event) { 42 | event.stopPropagation(); 43 | event.preventDefault(); 44 | 45 | // xxxHonza: how to get reference to the action list? 46 | /*this.props.actions.onViewSource({ 47 | url: topFrame.fileName, 48 | lineNumber: topFrame.lineNumber 49 | });*/ 50 | 51 | var frame = this.props.object; 52 | postChromeMessage("view-source", { 53 | url: frame.url, 54 | lineNumber: frame.lineNumber 55 | }); 56 | } 57 | }); 58 | 59 | // Registration 60 | 61 | function supportsObject(object/*, type*/) { 62 | return object instanceof StackFrame; 63 | } 64 | 65 | Reps.registerRep({ 66 | rep: React.createFactory(StackFrameRep), 67 | supportsObject: supportsObject 68 | }); 69 | 70 | // helper classes using in the packets reducers 71 | 72 | // StackTrace 73 | 74 | // xxxHonza: revisit frame filtering should and find solid way. 75 | function StackTrace(frames, packetType) { 76 | // Remove all frames before 'EventEmitter_emit()' frame. 77 | // These are part of the infrastructure (always there). 78 | if (packetType == "send") { 79 | //frames = filterFrames(frames, "DebuggerClient.requester/<"); 80 | //frames = filterFrames(frames, "EventEmitter_emit"); 81 | //frames = filterFrames(frames, "makeInfallible/<", true); 82 | } 83 | 84 | // Filter out empty frames 85 | frames = frames.filter(frame => !!frame.name); 86 | 87 | // Create StackFrame instances, so the right rep is used for 88 | // rendering (see {@StackFrameRep}. 89 | this.frames = frames.map(frame => new StackFrame(frame)); 90 | } 91 | 92 | StackTrace.prototype = { 93 | hasFrames: function() { 94 | return this.frames.length > 0; 95 | }, 96 | 97 | getTopFrame: function() { 98 | return this.hasFrames() ? this.frames[0] : null; 99 | }, 100 | }; 101 | 102 | // StackFrame 103 | 104 | function StackFrame(frame) { 105 | this.url = frame.fileName; 106 | this.lineNumber = frame.lineNumber; 107 | this.columnNumber = frame.columnNumber; 108 | this.name = frame.name; 109 | } 110 | 111 | StackFrame.prototype = { 112 | getLabel: function() { 113 | var path = this.url; 114 | var index = path ? path.lastIndexOf("/") : -1; 115 | var label = (index == -1) ? path : path.substr(index + 1); 116 | 117 | if (this.lineNumber) { 118 | label += ":" + this.lineNumber; 119 | } 120 | 121 | if (this.columnNumber) { 122 | label += ":" + this.columnNumber; 123 | } 124 | 125 | return label; 126 | }, 127 | 128 | getUrl: function() { 129 | return this.url; 130 | } 131 | }; 132 | 133 | // Helpers 134 | 135 | /* NOTE: currently unused */ 136 | /* 137 | function filterFrames(frames, pivot, onlyFirst) { 138 | var frame; 139 | 140 | if (onlyFirst) { 141 | for (frame of frames) { 142 | if (frame.name == pivot) { 143 | frames.shift(); 144 | } else { 145 | break; 146 | } 147 | } 148 | return frames; 149 | } 150 | 151 | var newFrames = []; 152 | var remove = true; 153 | for (frame of frames) { 154 | if (!remove) { 155 | newFrames.push(frame); 156 | } 157 | 158 | if (frame.name == pivot) { 159 | remove = false; 160 | } 161 | } 162 | 163 | // The pivot frame wasn't found. 164 | if (remove) { 165 | newFrames = frames; 166 | } 167 | 168 | return newFrames; 169 | } 170 | */ 171 | 172 | 173 | // Exports from this module 174 | exports.StackFrameRep = StackFrameRep; 175 | exports.StackTrace = StackTrace; 176 | exports.StackFrame = StackFrame; 177 | 178 | }); 179 | -------------------------------------------------------------------------------- /lib/toolbox-overlay.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | "use strict"; 4 | 5 | module.metadata = { 6 | "stability": "stable" 7 | }; 8 | 9 | // Add-on SDK 10 | const { components } = require("chrome"); 11 | const { Class } = require("sdk/core/heritage"); 12 | const simplePrefs = require("sdk/simple-prefs"); 13 | const { prefs } = simplePrefs; 14 | 15 | // Firebug SDK 16 | const { Trace/*, TraceError*/ } = require("firebug.sdk/lib/core/trace.js").get(module.id); 17 | /*const { Locale } = require("firebug.sdk/lib/core/locale.js");*/ 18 | const { ToolboxOverlay: ToolboxOverlayBase } = require("firebug.sdk/lib/toolbox-overlay.js"); 19 | const { TransportObserver } = require("./transport-observer.js"); 20 | const { Arr } = require("firebug.sdk/lib/core/array.js"); 21 | const { Options } = require("firebug.sdk/lib/core/options.js"); 22 | 23 | // RDP Inspector 24 | const { InspectorWindow } = require("./inspector-window.js"); 25 | 26 | /** 27 | * @overlay xxxHonza: TODO docs 28 | */ 29 | const ToolboxOverlay = Class( 30 | /** @lends ToolboxOverlay */ 31 | { 32 | extends: ToolboxOverlayBase, 33 | 34 | overlayId: "RdpInspectorToolboxOverlay", 35 | 36 | packetCache: null, 37 | listeners: [], 38 | 39 | // Initialization 40 | 41 | initialize: function(options) { 42 | ToolboxOverlayBase.prototype.initialize.apply(this, arguments); 43 | 44 | Trace.sysout("ToolboxOverlay.initialize;", options); 45 | 46 | // handler of packetCache toggling 47 | this.onToggleCache = this.onToggleCache.bind(this); 48 | 49 | // set initial value 50 | this.onToggleCache(); 51 | 52 | // register for prefs change 53 | simplePrefs.on("packetCacheEnabled", this.onToggleCache); 54 | 55 | // Transport Protocol 56 | this.onSendPacket = this.onSendPacket.bind(this); 57 | this.onReceivePacket = this.onReceivePacket.bind(this); 58 | 59 | // Initialize transport observer 60 | this.toolbox.target.makeRemote(); 61 | let client = this.toolbox.target.client; 62 | this.transportObserver = new TransportObserver({client: client}); 63 | this.transportObserver.addListener(this); 64 | 65 | // Number of removed packets from the cache that are out of limit. 66 | this.removedPackets = 0; 67 | }, 68 | 69 | destroy: function() { 70 | ToolboxOverlayBase.prototype.destroy.apply(this, arguments); 71 | 72 | Trace.sysout("ToolboxOverlay.destroy;", arguments); 73 | 74 | simplePrefs.off("packetCacheEnabled", this.onToggleCache); 75 | 76 | if (this.transportObserver) { 77 | this.removeTransportListener(this); 78 | this.transportObserver.destroy(); 79 | } 80 | 81 | if (this.inspector) { 82 | this.inspector.destroy(); 83 | } 84 | }, 85 | 86 | // Events 87 | 88 | onReady: function(options) { 89 | ToolboxOverlayBase.prototype.onReady.apply(this, arguments); 90 | 91 | Trace.sysout("ToolboxOverlay.onReady;", options); 92 | }, 93 | 94 | onToggleCache: function() { 95 | this.packetCache = prefs.packetCacheEnabled ? [] : null; 96 | }, 97 | 98 | // Transport Listener 99 | 100 | getPacketCache: function() { 101 | return { 102 | removedPackets: this.removedPackets, 103 | packets: this.packetCache 104 | }; 105 | }, 106 | 107 | addTransportListener: function(listener) { 108 | this.listeners.push(listener); 109 | 110 | // Stop caching as soon as the first listener is registered. 111 | // It's the {@InspectorWindow} object that is listening to 112 | // sent/received packets. The listener is added as soon as 113 | // the window is initialized. 114 | this.packetCache = null; 115 | }, 116 | 117 | removeTransportListener: function(listener) { 118 | Arr.remove(this.listeners, listener); 119 | }, 120 | 121 | onSendPacket: function(packet) { 122 | var data = { 123 | type: "send", 124 | packet: packet, 125 | time: (new Date()).getTime(), 126 | stack: this.getStack() 127 | }; 128 | 129 | this.appendPacket(data); 130 | 131 | this.listeners.forEach(listener => { 132 | listener.onSendPacket(data); 133 | }); 134 | }, 135 | 136 | onReceivePacket: function(packet) { 137 | var data = { 138 | type: "receive", 139 | packet: packet, 140 | time: (new Date()).getTime(), 141 | stack: this.getStack() 142 | }; 143 | 144 | this.appendPacket(data); 145 | 146 | this.listeners.forEach(listener => { 147 | listener.onReceivePacket(data); 148 | }); 149 | }, 150 | 151 | appendPacket: function(packet) { 152 | if (!this.packetCache) { 153 | return; 154 | } 155 | 156 | try { 157 | this.packetCache.push(packet); 158 | } catch (err) { 159 | Trace.sysout("ToolboxOverlay.appendPacket; ERROR" + err, err); 160 | 161 | // xxxHonza: create a new packet that displays an error in the 162 | // packet list UI. 163 | // this.packetCache.push({...}); 164 | return; 165 | } 166 | 167 | var limit = Options.getPref("extensions.rdpinspector.packetCacheSize"); 168 | while (this.packetCache.length > limit) { 169 | this.packetCache.shift(); 170 | this.removedPackets++; 171 | } 172 | }, 173 | 174 | getStack: function() { 175 | var frames = []; 176 | var stack = components.stack; 177 | while (stack) { 178 | frames.push({ 179 | name: stack.name, 180 | lineNumber: stack.lineNumber, 181 | columnNumber: stack.columnNumber, 182 | fileName: stack.filename 183 | }); 184 | stack = stack.asyncCaller || stack.caller; 185 | } 186 | return frames; 187 | }, 188 | 189 | // Commands 190 | 191 | openInspectorWindow: function(inspectorWindowName) { 192 | Trace.sysout("ToolboxOverlay.toggleInspector;"); 193 | 194 | if (!this.inspector) { 195 | this.inspector = new InspectorWindow(this, inspectorWindowName); 196 | } 197 | 198 | // Show or hide the popup panel. 199 | this.inspector.open(); 200 | 201 | return this.inspector; 202 | } 203 | }); 204 | 205 | // Exports from this module 206 | exports.ToolboxOverlay = ToolboxOverlay; 207 | -------------------------------------------------------------------------------- /lib/connection-list-window.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | "use strict"; 4 | 5 | module.metadata = { 6 | "stability": "experimental" 7 | }; 8 | 9 | // Add-on SDK 10 | const { data } = require("sdk/self"); 11 | /*const options = require("@loader/options");*/ 12 | const { Class } = require("sdk/core/heritage"); 13 | const { openDialog, getMostRecentWindow } = require("sdk/window/utils"); 14 | const Events = require("sdk/dom/events"); 15 | const { Cu } = require("chrome"); 16 | 17 | // Firebug SDK 18 | const { Trace, TraceError } = require("firebug.sdk/lib/core/trace.js").get(module.id); 19 | const { Locale } = require("firebug.sdk/lib/core/locale.js"); 20 | const { Content } = require("firebug.sdk/lib/core/content.js"); 21 | const { Str } = require("firebug.sdk/lib/core/string.js"); 22 | const { Options } = require("firebug.sdk/lib/core/options.js"); 23 | const { Dispatcher } = require("firebug.sdk/lib/dispatcher.js"); 24 | 25 | // Constants 26 | const { InspectorService } = require("./inspector-service.js"); 27 | 28 | const WINDOW_TYPE = "RDPInspectorConnectionList"; 29 | const CONNECTIONS_XUL_URL = "chrome://rdpinspector/content/connections-window.xul"; 30 | 31 | /** 32 | * This class manages the connections list window 33 | */ 34 | const ConnectionListWindow = Class( 35 | /** @lends ConnectionListWindow */ 36 | { 37 | initialize() { 38 | // TODO: bind event handling methods 39 | 40 | // DOM Events 41 | this.onClose = this.onClose.bind(this); 42 | this.onLoad = this.onLoad.bind(this); 43 | this.onFrameContentLoaded = this.onFrameContentLoaded.bind(this); 44 | 45 | // Content callbacks 46 | this.connectionsUpdatedListeners = new Set(); 47 | 48 | // Dispatcher events 49 | this.onRDPConnectionsUpdated = this.onRDPConnectionsUpdated.bind(this); 50 | 51 | Dispatcher.on("onRDPConnectionsUpdated", this.onRDPConnectionsUpdated); 52 | }, 53 | 54 | destroy() { 55 | // TODO: cleanup (e.g. close the connection list window) 56 | 57 | // Content callbacks 58 | this.connectionsUpdatedListeners.clear(); 59 | 60 | // Dispatcher events 61 | Dispatcher.off("onRDPConnectionsUpdated", this.onRDPConnectionsUpdated); 62 | }, 63 | 64 | open() { 65 | Trace.sysout("ConnectionListWindow.open;", arguments); 66 | 67 | // If the Connections List window is already opened, focus and return it. 68 | let win = getMostRecentWindow(WINDOW_TYPE); 69 | 70 | if (win) { 71 | win.focus(); 72 | return win; 73 | } 74 | 75 | return this._open("rdp-inspector-connections", CONNECTIONS_XUL_URL, [{}]); 76 | }, 77 | 78 | _open(windowType, url, params) { 79 | Trace.sysout("ConnectionListWindow.open;", arguments); 80 | 81 | // Open RDP Inspector console window. 82 | this.win = openDialog({ 83 | url: url, 84 | name: WINDOW_TYPE, 85 | args: params, 86 | features: "chrome,resizable,scrollbars=auto,minimizable,dialog=no" 87 | }); 88 | 89 | // Hook events 90 | Events.on(this.win, "load", this.onLoad); 91 | Events.on(this.win, "close", this.onClose); 92 | 93 | return this.win; 94 | }, 95 | 96 | // Event Handlers 97 | 98 | onLoad(event) { 99 | Trace.sysout("ConnectionListWindow.onLoad; ", event); 100 | 101 | let frame = this.getFrame(); 102 | frame.setAttribute("src", data.url("connection-list/index.html")); 103 | 104 | Events.once(frame, "DOMContentLoaded", this.onFrameContentLoaded); 105 | }, 106 | 107 | onFrameContentLoaded(event) { 108 | Trace.sysout("ConnectionListWindow.onFrameContentLoaded; ", event); 109 | 110 | let contentWindow = this.getContentWindow(); 111 | 112 | let { Trace: contentTrace } = FBTrace.get("CONTENT"); 113 | Content.exportIntoContentScope(contentWindow, Str, "Str"); 114 | Content.exportIntoContentScope(contentWindow, Locale, "Locale"); 115 | Content.exportIntoContentScope(contentWindow, contentTrace, "Trace"); 116 | Content.exportIntoContentScope(contentWindow, TraceError, "TraceError"); 117 | Content.exportIntoContentScope(contentWindow, Options, "Options"); 118 | 119 | this.exportCustomContentAPI(contentWindow); 120 | }, 121 | 122 | exportCustomContentAPI(contentWindow) { 123 | var contentAPI = Cu.createObjectIn(contentWindow, { 124 | defineAs: "RDPConnectionList" 125 | }); 126 | 127 | Cu.exportFunction(function getConnectionsInfo() { 128 | let connectionsInfo = InspectorService.getConnectionsInfo(); 129 | 130 | return Cu.cloneInto(connectionsInfo, contentWindow); 131 | }, contentAPI, { 132 | defineAs: "getConnectionsInfo" 133 | }); 134 | 135 | Cu.exportFunction(function openRDPInspectorWindow(conn) { 136 | InspectorService.openRDPInspectorWindow(conn.uuid); 137 | }, contentAPI, { 138 | defineAs: "openRDPInspectorWindow" 139 | }); 140 | 141 | var onConnectionsUpdated = Cu.createObjectIn(contentAPI, { 142 | defineAs: "onConnectionsUpdated" 143 | }); 144 | 145 | Cu.exportFunction((cb) => { 146 | this.connectionsUpdatedListeners.add(cb); 147 | }, onConnectionsUpdated, { allowCallbacks: true, defineAs: "addListener" }); 148 | 149 | Cu.exportFunction((cb) => { 150 | this.connectionsUpdatedListeners.delete(cb); 151 | }, onConnectionsUpdated, { allowCallbacks: true, defineAs: "removeListener" }); 152 | }, 153 | 154 | onRDPConnectionsUpdated() { 155 | let contentWindow = this.getContentWindow(); 156 | let connectionsInfo = InspectorService.getConnectionsInfo(); 157 | 158 | let contentConnInfo = Cu.cloneInto(connectionsInfo, contentWindow); 159 | 160 | for (let cb of this.connectionsUpdatedListeners) { 161 | try { 162 | cb(contentConnInfo); 163 | } catch(e) { 164 | TraceError.sysout("ConnectionListWindow.onRDPConnectionsUpdated; ERROR in content cb: " + e); 165 | } 166 | } 167 | }, 168 | 169 | onClose(event) { 170 | Trace.sysout("ConnectionListWindow.onClose; " + event, arguments); 171 | 172 | this.win = null; 173 | }, 174 | 175 | getContentWindow: function() { 176 | let frame = this.getFrame(); 177 | if (!frame) { 178 | return null; 179 | } 180 | 181 | return frame && frame.contentWindow; 182 | }, 183 | 184 | getFrame: function() { 185 | if (!this.win) { 186 | return null; 187 | } 188 | 189 | return this.win.document.getElementById("contentFrame"); 190 | } 191 | }); 192 | 193 | // Exports from this module 194 | exports.ConnectionListWindow = ConnectionListWindow; 195 | -------------------------------------------------------------------------------- /data/inspector/css/packets-panel.css: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | /******************************************************************************/ 4 | /* Packets Panel */ 5 | 6 | .packetPanel { 7 | margin-bottom: 6px; 8 | font-size: 11px; 9 | font-family: Lucida Grande, Tahoma, sans-serif; 10 | line-height: 15px; 11 | } 12 | 13 | .packetPanel .packetBox { 14 | display: inline-block; 15 | max-width: 90%; 16 | } 17 | 18 | .packetPanel .packetContent { 19 | display: flex; 20 | } 21 | 22 | .packetPanel.receive .boxArrow { 23 | background-image: url("../res/received-arrow.png"); 24 | background-repeat: no-repeat; 25 | background-position: 0 center; 26 | margin-right: -1px; 27 | order: 1; 28 | width: 8px; 29 | z-index: 2; 30 | min-width: 8px; 31 | } 32 | 33 | .packetPanel.receive.selected .boxArrow { 34 | background-image: url("../res/received-arrow-selected.png"); 35 | } 36 | 37 | .packetPanel.send .boxArrow { 38 | background-image: url("../res/sent-arrow.png"); 39 | background-repeat: no-repeat; 40 | background-position: 0 center; 41 | margin-left: -1px; 42 | order: 3; 43 | width: 9px; 44 | min-width: 9px; 45 | z-index: 2; 46 | } 47 | 48 | .packetPanel.send.selected .boxArrow { 49 | background-image: url("../res/sent-arrow-selected.png"); 50 | } 51 | 52 | .packetPanel .body { 53 | display: inline-block; 54 | padding: 2px 5px 2px 5px; 55 | border: 1px solid #bfccd1; 56 | border-radius: 3px; 57 | min-width: 270px; 58 | background-image: linear-gradient(rgba(255, 255, 255, 0.7), rgba(255, 255, 255, 0.0)); 59 | order: 2; 60 | z-index: 1; 61 | } 62 | 63 | .packetPanel.receive .body { 64 | background-color: rgb(252, 248, 227); 65 | } 66 | 67 | .packetPanel.send .body { 68 | background-color: rgb(223, 240, 216); 69 | text-align: left; 70 | } 71 | 72 | .packetPanel.send { 73 | text-align: left; 74 | } 75 | 76 | .packetPanel.send { 77 | text-align: right; 78 | } 79 | 80 | .packetPanel .body .text { 81 | color: gray; 82 | } 83 | 84 | .packetPanel.error .body { 85 | background-color: rgb(255, 221, 221); 86 | } 87 | 88 | .packetPanel.selected .body { 89 | border-color: rgb(0, 170, 214); 90 | background-color: rgb(173, 211, 232); 91 | } 92 | 93 | .packetPanel .type, 94 | .packetPanel .to, 95 | .packetPanel .from { 96 | color: red; 97 | font-family: monospace; 98 | } 99 | 100 | .packetPanel .info { 101 | color: gray; 102 | font-size: 10px; 103 | text-align: right; 104 | margin-left: 20px; 105 | display: inline-flex; 106 | font-family: monospace; 107 | float: right; 108 | padding-top: 4px; 109 | } 110 | 111 | .packetPanel .stack:hover { 112 | text-decoration: underline; 113 | } 114 | 115 | .packetPanel .arrow { 116 | margin: 0 5px 0 5px; 117 | } 118 | 119 | .packetPanel .arrow.hide { 120 | display: none; 121 | } 122 | .packetPanel .from { 123 | padding-bottom: 5px;} 124 | 125 | .packetPanel .errorMessage { 126 | color: red; 127 | margin-bottom: 5px; 128 | } 129 | 130 | /******************************************************************************/ 131 | /* Inline Packet Details Preview */ 132 | 133 | .packetPanel .preview { 134 | overflow: auto; 135 | width: 100%; 136 | } 137 | 138 | .packetPanel .memberRow:hover { 139 | background-color: transparent; 140 | } 141 | 142 | .packetPanel .domTable > tbody > tr > td { 143 | border-bottom: transparent; 144 | } 145 | 146 | /******************************************************************************/ 147 | 148 | .packetsPanelBox { 149 | height: 100%; 150 | } 151 | 152 | .packetsPanelBox TD { 153 | vertical-align: top; 154 | } 155 | 156 | .packetsPanelBox .list { 157 | /* xxxHonza: minus the toolbar height*/ 158 | height: calc(100% - 30px); 159 | overflow: auto; 160 | padding: 10px; 161 | } 162 | 163 | .packetsPanelBox .details { 164 | border-left: 1px solid #EFEFEF; 165 | height: 100%; 166 | overflow: auto; 167 | } 168 | 169 | .tabPacketsPane { 170 | height: 100%; 171 | padding: 0px; 172 | } 173 | 174 | /******************************************************************************/ 175 | /* Packets Summary */ 176 | 177 | .packetsSummary { 178 | color: gray; 179 | font-size: 10px; 180 | border-top: 1px dotted #EFEFEF; 181 | margin: 10px 0 20px 0; 182 | padding: 2px 0 2px 0; 183 | } 184 | 185 | .packetsSummary DIV { 186 | display: inline-block; 187 | } 188 | 189 | .packetsSummary .time { 190 | float: right; 191 | font-family: monospace; 192 | } 193 | 194 | .packetsSummary .separator { 195 | width: 20px; 196 | } 197 | 198 | .packetsSummary .text { 199 | padding-right: 3px; 200 | } 201 | 202 | .packetsSummary .slash { 203 | width: 10px; 204 | text-align: center; 205 | } 206 | 207 | .tooltip-inner { 208 | max-width: 110px; 209 | } 210 | 211 | /******************************************************************************/ 212 | /* Packets Limit */ 213 | 214 | .packetsLimit { 215 | text-align: center; 216 | margin: 10px 0 20px 0; 217 | } 218 | 219 | .packetsLimit .text { 220 | padding: 2px 3px 2px 3px; 221 | color: gray; 222 | font-size: 12px; 223 | border: 1px solid #bfccd1; 224 | border-radius: 3px; 225 | background-color: rgb(219, 234, 249) !important; 226 | background-image: linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.2)); 227 | } 228 | 229 | /******************************************************************************/ 230 | /* Packets Message */ 231 | 232 | .packetsMessage { 233 | color: gray; 234 | font-size: 10px; 235 | border-top: 1px dotted #EFEFEF; 236 | margin: 10px 0 20px 0; 237 | padding: 2px 0 2px 0; 238 | } 239 | 240 | .packetsMessage DIV { 241 | display: inline-block; 242 | } 243 | 244 | .packetsMessage .time { 245 | float: right; 246 | font-family: monospace; 247 | } 248 | 249 | .packetsMessage .text { 250 | padding-right: 3px; 251 | } 252 | 253 | /******************************************************************************/ 254 | /* StackFrame */ 255 | 256 | .stackSidePanel { 257 | height: 100%; 258 | overflow: auto; 259 | } 260 | 261 | .stackSidePanel .preview { 262 | overflow: auto; 263 | width: 100%; 264 | } 265 | 266 | .stackSidePanel .objectBox-stackFrame .stackName { 267 | display: inline-flex; 268 | font-family: monospace; 269 | float: left; 270 | text-align: left; 271 | } 272 | 273 | .packetPanel .stackFrameLabel, 274 | .stackSidePanel .objectBox-stackFrame .stackLabel { 275 | color: blue; 276 | text-align: right; 277 | display: inline-flex; 278 | font-family: monospace; 279 | float: right; 280 | cursor: pointer; 281 | margin-right: 4px; 282 | } 283 | 284 | .packetPanel .stackFrameLabel { 285 | font-size: 10px; 286 | margin-top: 4px; 287 | float: left; 288 | } 289 | -------------------------------------------------------------------------------- /lib/inspector-actor.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | "use strict"; 4 | 5 | // Add-on SDK 6 | const { Cu } = require("chrome"); 7 | 8 | /** 9 | * Allows importing a JS module (Firefox platform) and specify alternative locations 10 | * to keep backward compatibility in case when the module location changes. 11 | * It helps Firebug to support multiple Firefox versions. 12 | * 13 | * @param {Array} locations List of URLs to try when importing the module. 14 | * @returns Scope of the imported module or an empty scope if module wasn't successfully loaded. 15 | */ 16 | function safeImport(...args) { 17 | for (var i = 0; i < args.length; i++) { 18 | try { 19 | return Cu["import"](args[i], {}); 20 | } 21 | /* eslint-disable */ 22 | catch (err) { 23 | } 24 | /* eslint-enable */ 25 | } 26 | return {}; 27 | } 28 | 29 | function safeRequire(devtoolsLoader, ...args) { 30 | for (var i = 0; i < args.length; i++) { 31 | try { 32 | return devtoolsLoader["require"](args[i]); 33 | } 34 | /* eslint-disable */ 35 | catch (err) { 36 | } 37 | /* eslint-enable */ 38 | } 39 | return {}; 40 | } 41 | 42 | // DevTools 43 | // See also: 44 | // - https://bugzilla.mozilla.org/show_bug.cgi?id=912121 45 | // - https://bugzilla.mozilla.org/show_bug.cgi?id=1203159 46 | const devtools = safeImport( 47 | "resource://devtools/shared/Loader.jsm", 48 | "resource://gre/modules/devtools/shared/Loader.jsm", 49 | "resource://gre/modules/devtools/Loader.jsm" 50 | ).devtools; 51 | 52 | const protocol = safeRequire(devtools, 53 | "devtools/shared/protocol", 54 | "devtools/server/protocol" 55 | ); 56 | 57 | const { DebuggerServer } = devtools["require"]("devtools/server/main"); 58 | const { method, RetVal, ActorClass, Actor } = protocol; 59 | 60 | // For debugging purposes. Note that the tracing module isn't available 61 | // on the backend (in case of remote device debugging). 62 | // const baseUrl = "resource://rdpinspector-at-getfirebug-dot-com/"; 63 | // const { getTrace } = Cu.import(baseUrl + "node_modules/firebug.sdk/lib/core/actor.js"); 64 | // const Trace = getTrace(); 65 | const Trace = {sysout: () => {}}; 66 | 67 | function dumpFactories(factories) { 68 | let result = []; 69 | 70 | let props = Object.getOwnPropertyNames(factories); 71 | for (let name of props) { 72 | let factory = factories[name]; 73 | result.push({ 74 | name: factory.name, 75 | prefix: factory._prefix, 76 | constructor: factory.constructor ? factory.constructor.name : undefined 77 | }); 78 | } 79 | 80 | return result; 81 | } 82 | 83 | /** 84 | * Helper actor state watcher. 85 | */ 86 | function expectState(expectedState, calledMethod) { 87 | return function(...args) { 88 | if (this.state !== expectedState) { 89 | Trace.sysout("actor.expectState; ERROR wrong state, expected '" + 90 | expectedState + "', but current state is '" + this.state + "'" + 91 | ", method: " + calledMethod); 92 | 93 | let msg = "Wrong State: Expected '" + expectedState + "', but current " + 94 | "state is '" + this.state + "'"; 95 | 96 | return Promise.reject(new Error(msg)); 97 | } 98 | 99 | try { 100 | return calledMethod.apply(this, args); 101 | } catch (err) { 102 | Cu.reportError("actor.js; expectState EXCEPTION " + err, err); 103 | } 104 | }; 105 | } 106 | 107 | /** 108 | * @actor TODO docs 109 | */ 110 | var InspectorActor = ActorClass( 111 | /** @lends InspectorActor */ 112 | { 113 | typeName: "actorInspector", 114 | 115 | // Initialization 116 | 117 | initialize: function(conn, parent) { 118 | Trace.sysout("InspectorActor.initialize; parent: " + 119 | parent.actorID + ", conn: " + conn.prefix, this); 120 | 121 | Actor.prototype.initialize.call(this, conn); 122 | 123 | this.parent = parent; 124 | this.state = "detached"; 125 | }, 126 | 127 | /** 128 | * The destroy is only called automatically by the framework (parent actor) 129 | * if an actor is instantiated by a parent actor. 130 | */ 131 | destroy: function() { 132 | Trace.sysout("InspectorActor.destroy; state: " + this.state, arguments); 133 | 134 | if (this.state === "attached") { 135 | this.detach(); 136 | } 137 | 138 | Actor.prototype.destroy.call(this); 139 | }, 140 | 141 | /** 142 | * Automatically executed by the framework when the parent connection 143 | * is closed. 144 | */ 145 | disconnect: function() { 146 | Trace.sysout("InspectorActor.disconnect; state: " + this.state, arguments); 147 | 148 | if (this.state === "attached") { 149 | this.detach(); 150 | } 151 | }, 152 | 153 | /** 154 | * Attach to this actor. Executed when the front (client) is attaching 155 | * to this actor. 156 | */ 157 | attach: method(expectState("detached", function() { 158 | Trace.sysout("monitorActor.attach;", arguments); 159 | 160 | this.state = "attached"; 161 | }), { 162 | request: {}, 163 | response: { 164 | type: "attached" 165 | } 166 | }), 167 | 168 | /** 169 | * Detach from this actor. Executed when the front (client) detaches 170 | * from this actor. 171 | */ 172 | detach: method(expectState("attached", function() { 173 | Trace.sysout("monitorActor.detach;", arguments); 174 | 175 | this.state = "detached"; 176 | }), { 177 | request: {}, 178 | response: { 179 | type: "detached" 180 | } 181 | }), 182 | 183 | // Actor API 184 | 185 | /** 186 | * A test remote method. 187 | */ 188 | getActors: method(expectState("attached", function() { 189 | let result = {}; 190 | 191 | Trace.sysout("inspectorActor.getActors; connection ", this.conn); 192 | 193 | // Get actor pools (instances) 194 | if (this.conn._actorPool) { 195 | result.actorPool = { 196 | pool: dumpPool(this.conn._actorPool), 197 | id: this.conn._actorPool.id 198 | }; 199 | } 200 | 201 | result.extraPools = []; 202 | 203 | for (let pool of this.conn._extraPools) { 204 | if (pool !== this.conn._actorPool) { 205 | result.extraPools.push({ 206 | pool: dumpPool(pool), 207 | id: pool.id 208 | }); 209 | } 210 | } 211 | 212 | // Get actor factories (classes) 213 | result.factories = {}; 214 | result.factories.global = dumpFactories(DebuggerServer.globalActorFactories); 215 | result.factories.tab = dumpFactories(DebuggerServer.tabActorFactories); 216 | 217 | return result; 218 | }), { 219 | request: {}, 220 | response: RetVal("json") 221 | }) 222 | }); 223 | 224 | // Helpers 225 | 226 | function dumpPool(pool) { 227 | let result = []; 228 | 229 | if (pool._actors) { 230 | pool.forEach(actor => { 231 | result.push({ 232 | actorID: actor.actorID, 233 | actorPrefix: actor.actorPrefix, 234 | typeName: actor.typeName, 235 | parentID: actor._parentActor ? actor._parentActor.actorID : undefined, 236 | constructor: actor.constructor ? actor.constructor.name : undefined 237 | }); 238 | }); 239 | } else { 240 | // xxxHonza: there are actors (classes) stored as pools. 241 | // See also: https://bugzilla.mozilla.org/show_bug.cgi?id=1119790#c1 242 | result = {}; 243 | result.actorID = pool.actorID; 244 | result.actorPrefix = pool.actorPrefix; 245 | result.typeName = pool.typeName; 246 | result.parentID = pool.parentID; 247 | } 248 | 249 | return result; 250 | } 251 | 252 | // Exports from this module 253 | exports.InspectorActor = InspectorActor; 254 | -------------------------------------------------------------------------------- /data/inspector/reducers/packets.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | define(function(require, exports, module) { 4 | 5 | "use strict"; 6 | 7 | // RDP Window injected APIs 8 | const { Options, Locale } = require("shared/rdp-inspector-window"); 9 | const { StackTrace } = require("../components/stack-frame-rep"); 10 | 11 | // actions types 12 | const { types } = require("../actions/packets"); 13 | 14 | /** 15 | * Initial state definition 16 | */ 17 | const initialState = { 18 | paused: false, 19 | uniqueId: 0, 20 | options: {}, 21 | filter: null, 22 | editedPacket: null, 23 | selectedPacket: null, 24 | removedPackets: 0, 25 | filteredPackets: [], 26 | packets: [], 27 | summary: { 28 | data: { sent: 0, received: 0}, 29 | packets: { sent: 0, received: 0} 30 | }, 31 | error: null, 32 | }; 33 | 34 | function packetsReducer(state = initialState, action) { 35 | switch(action.type) { 36 | case types.IMPORT_PACKETS_FROMFILE: 37 | return importPacketsFromFile(state, action.data); 38 | case types.IMPORT_PACKETS_CACHE: 39 | return importPacketsCache(state, action.cache); 40 | case types.INIT_PACKETLIST_OPTIONS: 41 | return initPacketListOptions(state, action.options); 42 | case types.TOGGLE_PACKETLIST_OPTION: 43 | return togglePacketListOption(state, action.name); 44 | case types.TOGGLE_PACKETLIST_PAUSE: 45 | return togglePacketListPause(state); 46 | case types.APPEND_PACKET: 47 | return appendPacket(state, action.packetType, action.packetData); 48 | case types.APPEND_SUMMARY: 49 | return appendSummary(state, action.time); 50 | case types.APPEND_MESSAGE: 51 | return appendMessage(state, action.message, action.time); 52 | case types.SELECT_PACKET: 53 | return selectPacket(state, action.packet); 54 | case types.EDIT_PACKET: 55 | return editPacket(state, action.packet); 56 | case types.CLEAR_PACKET_LIST: 57 | return clearPacketList(state); 58 | case types.SET_PACKETLIST_ERROR: 59 | return setPacketListError(state, action.error); 60 | default: 61 | return state; 62 | } 63 | } 64 | 65 | module.exports = packetsReducer; 66 | 67 | // helpers 68 | 69 | function importPacketsFromFile(state, data) { 70 | let removedPackets = data.removedPackets || 0; 71 | let newState = clearPacketList(state); 72 | 73 | if (data.packets && data.packets.length > 0) { 74 | for (let packet of data.packets) { 75 | newState = appendToCollectedPackets(newState, packet); 76 | } 77 | } 78 | 79 | return Object.assign({}, newState, { removedPackets }); 80 | } 81 | 82 | function importPacketsCache(state, cache) { 83 | let removedPackets = cache.removedPackets || 0; 84 | let newState = state; 85 | 86 | if (cache.packets && cache.packets.length > 0) { 87 | for (let packet of cache.packets) { 88 | // TODO: Trace errors 89 | newState = appendPacket(newState, packet.type, packet); 90 | } 91 | } 92 | 93 | return Object.assign({}, newState, { removedPackets }); 94 | } 95 | 96 | function initPacketListOptions(state, options) { 97 | return Object.assign({}, state, { 98 | options 99 | }); 100 | } 101 | 102 | function togglePacketListOption(state, name) { 103 | let options = JSON.parse(JSON.stringify(state.options)); 104 | options[name] = !options[name]; 105 | 106 | return Object.assign({}, state, { 107 | options 108 | }); 109 | } 110 | 111 | function togglePacketListPause(state) { 112 | let paused = !state.paused; 113 | let newState; 114 | 115 | if (paused) { 116 | newState = appendMessage(state, Locale.$STR("rdpInspector.label.Paused"), new Date()); 117 | } else { 118 | newState = appendMessage(state, Locale.$STR("rdpInspector.label.Unpaused"), new Date()); 119 | } 120 | 121 | return Object.assign({}, newState, { 122 | paused 123 | }); 124 | } 125 | 126 | function appendToCollectedPackets(state, packet) { 127 | let limit = Options.getPref("extensions.rdpinspector.packetLimit"); 128 | 129 | if (state.packets.length >= limit) { 130 | state.packets.shift(); 131 | } 132 | 133 | let summary = JSON.parse(JSON.stringify(state.summary)); 134 | 135 | switch (packet.type) { 136 | case "send": 137 | summary.data.sent += packet.size; 138 | summary.packets.sent += 1; 139 | break; 140 | case "receive": 141 | summary.data.received += packet.size; 142 | summary.packets.received += 1; 143 | break; 144 | } 145 | 146 | packet.id = ++state.uniqueId; 147 | 148 | let packets = [ ...state.packets, packet]; 149 | let filteredPackets = filterRDPInspectPackets(packets); 150 | 151 | return Object.assign({}, state, { 152 | summary, packets, filteredPackets 153 | }); 154 | } 155 | 156 | function appendPacket(state, packetType, packetData) { 157 | let rawPacket = JSON.stringify(packetData.packet); 158 | let packet = { 159 | type: packetType, 160 | packet: packetData.packet, 161 | rawPacket, 162 | size: rawPacket.length, 163 | time: new Date(packetData.time), 164 | stack: new StackTrace(packetData.stack, packetType) 165 | }; 166 | 167 | return appendToCollectedPackets(state, packet); 168 | } 169 | 170 | function appendSummary(state, time) { 171 | return appendToCollectedPackets(state, { 172 | type: "summary", 173 | time, 174 | data: { 175 | sent: state.summary.data.sent, 176 | received: state.summary.data.received 177 | }, 178 | packets: { 179 | sent: state.summary.packets.sent, 180 | received: state.summary.packets.received 181 | } 182 | }); 183 | } 184 | 185 | function appendMessage(state, message, time) { 186 | return appendToCollectedPackets(state, { 187 | type: "message", time, message 188 | }); 189 | } 190 | 191 | function selectPacket(state, packet) { 192 | return Object.assign({}, state, { 193 | selectedPacket: packet 194 | }); 195 | } 196 | 197 | function editPacket(state, packet) { 198 | return Object.assign({}, state, { 199 | editedPacket: packet 200 | }); 201 | } 202 | 203 | function clearPacketList(state) { 204 | return Object.assign({}, state, { 205 | selectedPacket: null, 206 | packets: [], 207 | filteredPackets: [], 208 | summary: JSON.parse(JSON.stringify(initialState.summary)) 209 | }); 210 | } 211 | 212 | function setPacketListError(state, error) { 213 | // TODO: Trace errors 214 | return Object.assign({}, state, { error }); 215 | } 216 | 217 | function filterRDPInspectPackets(packets) { 218 | var filterFrom = {}; 219 | 220 | return packets.filter((packet) => { 221 | var actorId = packet.packet ? (packet.packet.to || packet.packet.from) : null; 222 | 223 | // filter our all the RDPi actorInspector actor 224 | if (actorId && actorId.indexOf("actorInspector") > 0) { 225 | return false; 226 | } 227 | 228 | if (packet.type == "send") { 229 | // filter sent RDP packets needed to register the RDPi actorInspector actor 230 | if (packet.packet.rdpInspectorInternals) { 231 | filterFrom[packet.packet.to] = filterFrom[packet.packet.to] || 0; 232 | filterFrom[packet.packet.to] += 1; 233 | 234 | return false; 235 | } 236 | 237 | // filter sent RDP packets needed to register the RDPi actorInspector actor 238 | if (packet.packet.type == "registerActor" && 239 | packet.packet.filename.indexOf("rdpinspector-at-getfirebug-dot-com") > 0) { 240 | filterFrom[packet.packet.to] = filterFrom[packet.packet.to] || 0; 241 | filterFrom[packet.packet.to] += 1; 242 | return false; 243 | } 244 | } 245 | 246 | // filter received RDP packets needed to register the RDPi actorInspector actor 247 | if (packet.type == "receive" && filterFrom[packet.packet.from] > 0) { 248 | filterFrom[packet.packet.from] -= 1; 249 | return false; 250 | } 251 | 252 | return true; 253 | }); 254 | } 255 | 256 | }); 257 | -------------------------------------------------------------------------------- /data/inspector/components/packet.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | define(function(require, exports /*, module */) { 4 | 5 | "use strict"; 6 | 7 | // ReactJS 8 | const React = require("react"); 9 | 10 | // Firebug SDK 11 | const { TreeView } = require("reps/tree-view"); 12 | 13 | // RDP Inspector 14 | //const { TextWithTooltip } = require("./text-with-tooltip"); 15 | 16 | // Constants 17 | const { div, span, ul, li, a } = React.DOM; 18 | 19 | // RDP Window injected APIs 20 | const { Locale, Str } = require("shared/rdp-inspector-window"); 21 | 22 | /** 23 | * @template This template is responsible for rendering a packet. 24 | * Packets are rendered within {@link PacketList} sorted by time. 25 | * 26 | * A packet displays basic information in the list and can also 27 | * display inline preview of all inner fields. 28 | */ 29 | var Packet = React.createClass({ 30 | /** @lends Packet */ 31 | 32 | displayName: "Packet", 33 | 34 | /** 35 | * Packet needs to be re-rendered only if the selection or 36 | * 'show inline details' option changes. This is an optimization 37 | * the makes the packet-list rendering a lot faster. 38 | */ 39 | shouldComponentUpdate: function(nextProps, nextState) { 40 | var { contextMenu: prevContextMenu } = this.state || {}; 41 | var { contextMenu: nextContextMenu } = nextState || {}; 42 | 43 | return (this.props.selected != nextProps.selected || 44 | this.props.showInlineDetails != nextProps.showInlineDetails || 45 | prevContextMenu != nextContextMenu); 46 | }, 47 | 48 | render: function() { 49 | var data = this.props.data; 50 | var packet = data.packet; 51 | var type = packet.type ? packet.type : ""; 52 | var mode = "tiny"; 53 | var classNames = ["packetPanel", data.type]; 54 | var size = Str.formatSize(data.size); 55 | var time = data.time; 56 | //var stack = data.stack; 57 | 58 | // Use String.formatTime, but how to access from the content? 59 | var timeText = time.toLocaleTimeString() + "." + time.getMilliseconds(); 60 | var previewData = { 61 | packet: packet 62 | }; 63 | 64 | // Error packets have its own styling 65 | if (packet.error) { 66 | classNames.push("error"); 67 | } 68 | 69 | // Selected packets are highlighted 70 | if (this.props.selected) { 71 | classNames.push("selected"); 72 | } 73 | 74 | /*var topFrame = stack.getTopFrame(); 75 | var stackFrameUrl = topFrame ? topFrame.getUrl() : null; 76 | var stackFrame = topFrame ? topFrame.getLabel() : null;*/ 77 | 78 | // Inline preview component 79 | var preview = this.props.showInlineDetails ? TreeView( 80 | {data: previewData, mode: mode}) : null; 81 | 82 | if (this.props.data.type == "send") { 83 | return ( 84 | div({className: classNames.join(" "), onClick: this.onClick, 85 | onContextMenu: this.onContextMenu}, 86 | div({className: "packetBox"}, 87 | div({className: "packetContent"}, 88 | div({className: "body"}, 89 | span({className: "text"}, 90 | Locale.$STR("rdpInspector.label.sent") + " " 91 | ), 92 | span({className: "type"}, type), 93 | span({className: "text"}, 94 | " " + Locale.$STR("rdpInspector.label.to") + " " 95 | ), 96 | span({className: "to"}, packet.to), 97 | div({}, 98 | /*TextWithTooltip({ 99 | tooltip: stackFrameUrl, className: "stackFrameLabel", 100 | onClick: this.onViewSource.bind(this, topFrame)}, 101 | stackFrame 102 | ),*/ 103 | span({className: "info"}, timeText + ", " + size) 104 | ), 105 | div({className: "preview"}, 106 | preview 107 | ) 108 | ), 109 | div({className: "boxArrow"}) 110 | ) 111 | ), 112 | this.state && this.state.contextMenu && 113 | ul({className: "dropdown-menu", role: "menu", ref: "contextMenu", 114 | onMouseLeave: this.onContextMenuMouseLeave, 115 | style: { 116 | display: "block", 117 | top: this.state.contextMenuTop, 118 | left: this.state.contextMenuLeft 119 | }}, 120 | li({role: "presentation"}, 121 | a({ref: "editAndResendAction", onClick: this.onEditAndResendClick}, 122 | "Edit and Resend")) 123 | ) 124 | ) 125 | ); 126 | } else { 127 | return ( 128 | div({className: classNames.join(" "), onClick: this.onClick}, 129 | div({className: "packetBox"}, 130 | div({className: "packetContent"}, 131 | div({className: "boxArrow"}), 132 | div({className: "body"}, 133 | div({className: "from"}, 134 | span({className: "text"}, 135 | Locale.$STR("rdpInspector.label.received") + " " 136 | ), 137 | span({}, type), 138 | span({className: "text"}, 139 | " " + Locale.$STR("rdpInspector.label.from") + " "), 140 | span({}, packet.from), 141 | div({}, 142 | /*TextWithTooltip({ 143 | tooltip: stackFrameUrl, className: "stackFrameLabel", 144 | onClick: this.onViewSource.bind(this, topFrame)}, 145 | stackFrame 146 | ),*/ 147 | span({className: "info"}, timeText + ", " + size) 148 | ) 149 | ), 150 | // NOTE: on issue #44, a long "consoleAPICall" received packet 151 | // was wrongly turned into a "div.errorMessage" 152 | packet.error ? div({className: "errorMessage"}, 153 | div({}, packet.error), 154 | div({}, packet.message) 155 | ) : null, 156 | div({className: "preview"}, 157 | preview 158 | ) 159 | ) 160 | ) 161 | ) 162 | ) 163 | ); 164 | } 165 | }, 166 | 167 | // Event Handlers 168 | onEditAndResendClick: function() { 169 | this.setState({ 170 | contextMenu: false 171 | }); 172 | this.props.actions.editPacket(this.props.data.packet); 173 | }, 174 | 175 | onContextMenuMouseLeave: function() { 176 | this.setState({ 177 | contextMenu: false 178 | }); 179 | }, 180 | 181 | onContextMenu: function(event) { 182 | event.stopPropagation(); 183 | event.preventDefault(); 184 | 185 | this.setState({ 186 | contextMenu: true, 187 | contextMenuTop: event.clientY - 16, 188 | contextMenuLeft: event.clientX - 16 189 | }); 190 | this.props.actions.selectPacket(this.props.data); 191 | }, 192 | 193 | onClick: function(event) { 194 | var target = event.target; 195 | 196 | event.stopPropagation(); 197 | event.preventDefault(); 198 | 199 | // If a 'memberLabel' is clicked inside the inline preview 200 | // tree, let's process it by the tree, so expansion and 201 | // collapsing works. Otherwise just select the packet. 202 | if (!target.classList.contains("memberLabel")) { 203 | this.props.actions.selectPacket(this.props.data); 204 | } 205 | }, 206 | 207 | onViewSource: function(topFrame, event) { 208 | event.stopPropagation(); 209 | event.preventDefault(); 210 | 211 | this.props.actions.onViewSource({ 212 | url: topFrame.url, 213 | lineNumber: topFrame.lineNumber 214 | }); 215 | } 216 | }); 217 | 218 | // Exports from this module 219 | exports.Packet = React.createFactory(Packet); 220 | exports.PacketComponent = Packet; 221 | }); 222 | -------------------------------------------------------------------------------- /data/shared/rdp-inspector-window.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | define(function(require, exports/*, module*/) { 4 | 5 | "use strict"; 6 | 7 | if (["http:", "https:", "file:"].indexOf(window.location.protocol) >= 0) { 8 | // NOTE: add RDPi injected APIs shims, to be able to run the React UI in a tab 9 | exports.Locale = window.Locale = { 10 | $STR: function(s) { return s; } 11 | }; 12 | 13 | exports.Options = window.Options = { 14 | getPref: function(key) { 15 | /* eslint no-console: 0 */ 16 | switch(key) { 17 | case "extensions.rdpinspector.packetLimit": 18 | return 100; 19 | default: 20 | throw Error("UNKNOWN Option.getPref: " + key); 21 | } 22 | } 23 | }; 24 | 25 | exports.Str = window.Str = { 26 | formatSize: function(str) { return str; } 27 | }; 28 | 29 | exports.Trace = window.Trace = { 30 | sysout: function(...args) { 31 | console.log.apply(console, args); 32 | } 33 | }; 34 | 35 | exports.postChromeMessage = window.postChromeMessage = function() { 36 | console.log("POST CHROME MESSAGE", arguments); 37 | }; 38 | 39 | exports.RDPConnectionList = { 40 | getConnectionsInfo: () => {}, 41 | onConnectionsUpdated: { 42 | addListener: function() {}, 43 | removeListener: function() {} 44 | }, 45 | openRDPInspectorWindow: () => {} 46 | }; 47 | 48 | // inject domTree css with a relative url (chrom urls can't be 49 | // loaded in a page not loaded from a resource or chrome urls) 50 | 51 | const domTreeStylesheet = document.createElement("link"); 52 | domTreeStylesheet.setAttribute("href", "../../node_modules/firebug.sdk/skin/classic/shared/domTree.css"); 53 | domTreeStylesheet.setAttribute("rel", "stylesheet"); 54 | 55 | document.querySelector("head").appendChild(domTreeStylesheet); 56 | } else { 57 | /* globals Str, Locale, Options, Trace, postChromeMessage */ 58 | exports.Str = Str; 59 | exports.Locale = Locale; 60 | exports.Options = Options; 61 | exports.Trace = Trace; 62 | 63 | if ("postChromeMessage" in window) { 64 | exports.postChromeMessage = window.postChromeMessage; 65 | } 66 | 67 | if ("RDPConnectionList" in window) { 68 | exports.RDPConnectionList = window.RDPConnectionList; 69 | } 70 | } 71 | 72 | function RDPInspectorView({ window, actions, store }) { 73 | this.win = window; 74 | this.actions = actions; 75 | this.store = store; 76 | 77 | // subscribe window events 78 | let subscriptions = [ 79 | [ "init-options", "onInitOptions" ], 80 | [ "init-packet-list", "onInitPacketList" ], 81 | [ "send-packet", "onSendPacket" ], 82 | [ "receive-packet", "onReceivePacket" ], 83 | [ "loaded-packet-list-file", "onLoadedPacketListFile" ], 84 | [ "got-rdp-actors", "onGotRDPActors" ] 85 | ]; 86 | 87 | for (let [ message, handler ] of subscriptions) { 88 | this[handler] = this[handler].bind(this); 89 | this.win.addEventListener(message, this[handler], false); 90 | } 91 | } 92 | 93 | exports.RDPInspectorView = RDPInspectorView; 94 | 95 | RDPInspectorView.prototype = { 96 | // listeners 97 | onInitOptions(event) { 98 | let options = JSON.parse(event.data); 99 | let { store, actions } = this; 100 | store.dispatch(actions.initPacketListOptions(options)); 101 | }, 102 | 103 | onInitPacketList(event) { 104 | let cache = JSON.parse(event.data); 105 | let { store, actions } = this; 106 | store.dispatch(actions.importPacketsCache(cache)); 107 | store.dispatch(actions.appendSummary()); 108 | }, 109 | 110 | onSendPacket(event) { 111 | let packetData = JSON.parse(event.data); 112 | let { store, actions } = this; 113 | store.dispatch(actions.appendPacket("send", packetData)); 114 | }, 115 | 116 | onReceivePacket(event) { 117 | let packetData = JSON.parse(event.data); 118 | let { store, actions } = this; 119 | store.dispatch(actions.appendPacket("receive", packetData)); 120 | }, 121 | 122 | onGotRDPActors(event) { 123 | let actors = JSON.parse(event.data); 124 | let { store, actions } = this; 125 | store.dispatch(actions.setActors(actors)); 126 | }, 127 | 128 | onLoadedPacketListFile(event) { 129 | let { store, actions } = this; 130 | 131 | try { 132 | var data = deserializePacketsStore(event.data); 133 | store.dispatch(actions.importPacketsFromFile(data)); 134 | } catch(e) { 135 | store.dispatch(actions.setPacketListError({ 136 | message: "Error loading packets from file", 137 | details: e 138 | })); 139 | } 140 | }, 141 | 142 | // app event handlers 143 | 144 | clearError() { 145 | let { store, actions } = this; 146 | store.dispatch(actions.setPacketListError(null)); 147 | }, 148 | 149 | selectPacket(packet) { 150 | let { store, actions } = this; 151 | store.dispatch(actions.selectPacket(packet)); 152 | }, 153 | 154 | editPacket(packet) { 155 | let { store, actions } = this; 156 | store.dispatch(actions.editPacket(packet)); 157 | // TODO: remove this workaround 158 | window.dispatchEvent(new CustomEvent("rdpinspector:switchToPacketEditorTab")); 159 | }, 160 | 161 | clear() { 162 | let { store, actions } = this; 163 | store.dispatch(actions.clearPacketList()); 164 | }, 165 | 166 | find() { 167 | postChromeMessage("find"); 168 | }, 169 | 170 | send(packet) { 171 | postChromeMessage("inject-rdp-packet", packet); 172 | }, 173 | 174 | getActors() { 175 | postChromeMessage("get-rdp-actors"); 176 | }, 177 | 178 | appendSummary() { 179 | let { store, actions } = this; 180 | store.dispatch(actions.appendSummary()); 181 | 182 | // Auto scroll to the bottom, so the new summary is 183 | // immediately visible. 184 | var node = document.querySelector(".packetsPanelBox .list"); 185 | node.scrollTop = node.scrollHeight; 186 | }, 187 | 188 | onShowInlineDetails() { 189 | let { store, actions } = this; 190 | store.dispatch(actions.togglePacketListOption("showInlineDetails")); 191 | postChromeMessage("options-toggle", { name: "showInlineDetails"}); 192 | }, 193 | 194 | onPacketCacheEnabled() { 195 | let { store, actions } = this; 196 | store.dispatch(actions.togglePacketListOption("packetCacheEnabled")); 197 | postChromeMessage("options-toggle", { name: "packetCacheEnabled"}); 198 | }, 199 | 200 | onPause() { 201 | let { store, actions } = this; 202 | store.dispatch(actions.togglePacketListPause()); 203 | postChromeMessage("pause", store.getState().packets.paused); 204 | }, 205 | 206 | loadPacketsFromFile() { 207 | postChromeMessage("load-from-file"); 208 | }, 209 | 210 | savePacketsToFile() { 211 | let { store, actions } = this; 212 | try { 213 | var json = serializePacketsStore(store.getState().packets); 214 | } catch(e) { 215 | store.dispatch(actions.setPacketListError({ 216 | message: "Error saving packets to file", 217 | details: e 218 | })); 219 | return; 220 | } 221 | 222 | postChromeMessage("save-to-file", { 223 | data: json, 224 | contentType: "application/json", 225 | filename: "RDP-packets-dump.json" 226 | }); 227 | }, 228 | 229 | onViewSource(sourceLink) { 230 | postChromeMessage("view-source", sourceLink); 231 | }, 232 | }; 233 | 234 | // serialize / deserialze helpers 235 | 236 | const DUMP_FORMAT_VERSION = "rdp-inspector/packets-store/v1"; 237 | const DUMP_FORMAT_KEYS = [ 238 | "packets", "summary", 239 | "uniqueId", "removedPackets" 240 | ]; 241 | 242 | function serializePacketsStore(packetsStore) { 243 | var data = { 244 | "!format!": DUMP_FORMAT_VERSION 245 | }; 246 | DUMP_FORMAT_KEYS.forEach((key) => { 247 | data[key] = packetsStore[key]; 248 | }); 249 | return JSON.stringify(data); 250 | } 251 | 252 | function deserializePacketsStore(rawdata) { 253 | var data = JSON.parse(rawdata, (k, v) => { 254 | switch (k) { 255 | case "time": 256 | return new Date(v); 257 | default: 258 | return v; 259 | } 260 | }); 261 | 262 | var res = {}; 263 | 264 | if (data["!format!"] && 265 | data["!format!"] === DUMP_FORMAT_VERSION) { 266 | DUMP_FORMAT_KEYS.forEach((key) => { 267 | res[key] = data[key]; 268 | }); 269 | } else { 270 | throw Error("Dump file format unrecognized"); 271 | } 272 | 273 | return res; 274 | } 275 | 276 | }); 277 | -------------------------------------------------------------------------------- /data/inspector/components/actors-panel.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | define(function(require, exports/*, module*/) { 4 | 5 | "use strict"; 6 | 7 | // Dependencies 8 | const React = require("react"); 9 | 10 | // RDP Inspector 11 | const { ActorsToolbar } = require("./actors-toolbar"); 12 | 13 | const { Locale } = require("shared/rdp-inspector-window"); 14 | 15 | // Shortcuts 16 | const { tr, td, table, tbody, thead, th, div, h4 } = React.DOM; 17 | 18 | const GLOBAL_ACTORS_POOLS = "global-actors-pool"; 19 | const TAB_ACTORS_POOLS = "tab-actors-pool"; 20 | const ACTORS_FACTORIES = "actors-factories"; 21 | 22 | var PanelTypesLabels = {}; 23 | PanelTypesLabels[GLOBAL_ACTORS_POOLS] = Locale.$STR("rdpInspector.label.MainProcess"); 24 | PanelTypesLabels[TAB_ACTORS_POOLS] = Locale.$STR("rdpInspector.label.ChildProcess"); 25 | PanelTypesLabels[ACTORS_FACTORIES] = Locale.$STR("rdpInspector.label.ActorsFactories"); 26 | 27 | /** 28 | * @template This template renders 'Actors' tab body. 29 | */ 30 | var ActorsPanel = React.createClass({ 31 | /** @lends ActorsPanel */ 32 | 33 | displayName: "ActorsPanel", 34 | 35 | getInitialState: function() { 36 | return { 37 | panelType: GLOBAL_ACTORS_POOLS 38 | }; 39 | }, 40 | 41 | render: function() { 42 | var { actors } = this.props; 43 | var { panelType } = this.state; 44 | 45 | var el; 46 | 47 | switch(panelType) { 48 | case GLOBAL_ACTORS_POOLS: 49 | el = ActorsPools({ 50 | data: (actors && actors.global), 51 | key: "global", 52 | searchFilter: this.props.searchFilter 53 | }); 54 | break; 55 | case TAB_ACTORS_POOLS: 56 | el = ActorsPools({ 57 | data: (actors && actors.tab), 58 | key: "tab", 59 | searchFilter: this.props.searchFilter 60 | }); 61 | break; 62 | case ACTORS_FACTORIES: 63 | el = ActorsFactories({ 64 | key: "factories", 65 | data: { 66 | global: actors && actors.global, 67 | tab: actors && actors.tab 68 | }, 69 | searchFilter: this.props.searchFilter 70 | }); 71 | break; 72 | } 73 | 74 | return ( 75 | div({ className: "actorsPanelBox" }, 76 | ActorsToolbar({ actions: this.props.actions, 77 | currentPanelType: panelType, 78 | panelTypesLabels: PanelTypesLabels, 79 | onPanelTypeSelected: this.onPanelTypeSelected 80 | }), 81 | div({ className: "actorsScrollBox" }, el) 82 | ) 83 | ); 84 | }, 85 | 86 | onPanelTypeSelected: function(panelType) { 87 | this.setState({ 88 | panelType: panelType 89 | }); 90 | } 91 | }); 92 | 93 | /** 94 | * @template This template renders 'ActorsPools' list in the tab content. 95 | */ 96 | var ActorsPools = React.createFactory(React.createClass({ 97 | /** @lends ActorsPools */ 98 | 99 | displayName: "ActorsPools", 100 | 101 | render: function() { 102 | var pools = []; 103 | 104 | if (this.props.data) { 105 | var { actorPool, extraPools } = this.props.data; 106 | pools = pools.concat(actorPool, extraPools); 107 | } 108 | 109 | var poolTables = pools.map((poolData) => { 110 | var pool = poolData.pool; 111 | var poolId = poolData.id; 112 | var actorClass = false; 113 | 114 | if (!Array.isArray(pool)) { 115 | pool = [pool]; 116 | actorClass = true; 117 | } 118 | 119 | if (pool.length > 0) { 120 | return PoolTable({ 121 | pool: pool, 122 | actorClass: actorClass, 123 | id: poolId, 124 | key: poolId, 125 | searchFilter: this.props.searchFilter 126 | }); 127 | } 128 | }).filter((el) => el); 129 | 130 | return div({ className: "poolContainer" }, poolTables); 131 | } 132 | })); 133 | 134 | /** 135 | * @template This template renders 'PoolTable' which render a table related 136 | * to a single pool of actors. 137 | */ 138 | var PoolTable = React.createFactory(React.createClass({ 139 | /** @lends ActorsPanel */ 140 | 141 | displayName: "PoolTable", 142 | 143 | render: function() { 144 | var rows = []; 145 | 146 | // Iterate array of actors. 147 | var actors = this.props.pool; 148 | for (var i in actors) { 149 | if (this.props.searchFilter && 150 | JSON.stringify(actors[i]).toLowerCase() 151 | .indexOf(this.props.searchFilter.toLowerCase()) < 0) { 152 | // filter out packets which don't match the filter 153 | continue; 154 | } 155 | actors[i].key = actors[i].actorID; 156 | rows.push(PoolRow(actors[i])); 157 | } 158 | 159 | // Pools are mixed with Actor objects (created using CreateClass). 160 | var className = "poolTable"; 161 | if (this.props.actorClass) { 162 | className += " actorClass"; 163 | } 164 | 165 | var id = this.props.id ? "ID: " + this.props.id : ""; 166 | 167 | return ( 168 | div({}, 169 | h4({}, "Pool" + id), 170 | table({className: className}, 171 | thead({className: "poolRow"}, 172 | th({width: "20%"}, "Actor ID"), 173 | th({width: "20%"}, "Prefix"), 174 | th({width: "20%"}, "TypeName"), 175 | th({width: "20%"}, "Parent"), 176 | th({width: "20%"}, "Constructor") 177 | ), 178 | tbody(null, rows) 179 | ) 180 | ) 181 | ); 182 | } 183 | })); 184 | 185 | /** 186 | * @template This template renders a single row of the 'PoolTable' . 187 | */ 188 | var PoolRow = React.createFactory(React.createClass({ 189 | /** @lends PoolRow */ 190 | 191 | displayName: "PoolRow", 192 | 193 | render: function() { 194 | var actor = this.props; 195 | return ( 196 | tr({className: "poolRow"}, 197 | td({}, actor.actorID), 198 | td({}, actor.actorPrefix), 199 | td({}, actor.typeName), 200 | td({}, actor.parentID), 201 | td({}, actor.constructor) 202 | ) 203 | ); 204 | } 205 | })); 206 | 207 | /** 208 | * @template This template renders a single 'FactoryTable' component. 209 | */ 210 | var FactoryTable = React.createFactory(React.createClass({ 211 | /** @lends FactoryTable */ 212 | 213 | displayName: "FactoryTable", 214 | 215 | render: function() { 216 | var rows = []; 217 | 218 | var factories = this.props.factories; 219 | for (var i in factories) { 220 | if (this.props.searchFilter && 221 | JSON.stringify(factories[i]).toLowerCase() 222 | .indexOf(this.props.searchFilter.toLowerCase()) < 0) { 223 | // filter out packets which don't match the filter 224 | continue; 225 | } 226 | 227 | factories[i].key = factories[i].prefix + factories[i].name; 228 | rows.push(FactoryRow(factories[i])); 229 | } 230 | 231 | return ( 232 | table({className: "poolTable"}, 233 | thead({className: "poolRow"}, 234 | th({width: "33%"}, "Name"), 235 | th({width: "33%"}, "Prefix"), 236 | th({width: "33%"}, "Constructor") 237 | ), 238 | tbody(null, rows) 239 | ) 240 | ); 241 | } 242 | })); 243 | 244 | /** 245 | * @template This template renders the 'ActorsFactories' list of tables. 246 | */ 247 | var ActorsFactories = React.createFactory(React.createClass({ 248 | /** @lends ActorsFactories */ 249 | 250 | displayName: "ActorsFactories", 251 | 252 | render: function() { 253 | var main = this.props.data.global || { factories: {} }; 254 | var child = this.props.data.tab || { factories: {} }; 255 | var searchFilter = this.props.searchFilter; 256 | 257 | return ( 258 | div({className: "poolContainer"}, 259 | h4(null, "Main Process - Global Factories"), 260 | FactoryTable({ factories: main.factories.global, searchFilter: searchFilter }), 261 | h4(null, "Main Process - Tab Factories"), 262 | FactoryTable({ factories: main.factories.tab, searchFilter: searchFilter }), 263 | h4(null, "Child Process - Global Factories"), 264 | FactoryTable({ factories: child.factories.global, searchFilter: searchFilter }), 265 | h4(null, "Child Process - Tab Factories"), 266 | FactoryTable({ factories: child.factories.tab, searchFilter: searchFilter }) 267 | ) 268 | ); 269 | } 270 | })); 271 | 272 | /** 273 | * @template This template renders a single row of the 'FactoryTable'. 274 | */ 275 | var FactoryRow = React.createFactory(React.createClass({ 276 | /** @lends ActorsPanel */ 277 | 278 | displayName: "FactoryRow", 279 | 280 | render: function() { 281 | var factory = this.props; 282 | return ( 283 | tr({className: "poolRow"}, 284 | td({}, factory.name), 285 | td({}, factory.prefix), 286 | td({}, factory.ctor) 287 | ) 288 | ); 289 | } 290 | })); 291 | 292 | // Exports from this module 293 | exports.ActorsPanel = React.createFactory(ActorsPanel); 294 | exports.ActorsPanelComponent = ActorsPanel; 295 | 296 | }); 297 | -------------------------------------------------------------------------------- /lib/start-button.js: -------------------------------------------------------------------------------- 1 | /* See license.txt for terms of usage */ 2 | 3 | "use strict"; 4 | 5 | module.metadata = { 6 | "stability": "stable" 7 | }; 8 | 9 | // Add-on SDK 10 | const self = require("sdk/self"); 11 | const options = require("@loader/options"); 12 | const { prefs } = require("sdk/simple-prefs"); 13 | const { Cu } = require("chrome"); 14 | const { getMostRecentBrowserWindow } = require("sdk/window/utils"); 15 | const { defer } = require("sdk/core/promise"); 16 | const { openTab } = require("sdk/tabs/utils"); 17 | const { getNodeView } = require("sdk/view/core"); 18 | const { Class } = require("sdk/core/heritage"); 19 | 20 | // Firebug.SDK 21 | const { Trace/*, TraceError*/ } = require("firebug.sdk/lib/core/trace.js").get(module.id); 22 | const { Locale } = require("firebug.sdk/lib/core/locale.js"); 23 | const { ToolbarButton } = require("firebug.sdk/lib/toolbar-button.js"); 24 | const { Dispatcher } = require("firebug.sdk/lib/dispatcher.js"); 25 | 26 | // Platform 27 | const { CustomizableUI } = Cu.import("resource:///modules/CustomizableUI.jsm", {}); 28 | const { AREA_PANEL, AREA_NAVBAR } = CustomizableUI; 29 | const { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {}); 30 | 31 | // DevTools 32 | // See also: https://bugzilla.mozilla.org/show_bug.cgi?id=912121 33 | const { devtools, gDevTools } = require("firebug.sdk/lib/core/devtools.js"); 34 | 35 | // RDP Inspector 36 | const { ConnectionListWindow } = require("./connection-list-window"); 37 | 38 | const startButtonId = "rdp-inspector-start-button"; 39 | 40 | // Helpers 41 | 42 | function showToolbox(toolId) { 43 | let browser = getMostRecentBrowserWindow(); 44 | let tab = browser.gBrowser.mCurrentTab; 45 | let target = devtools.TargetFactory.forTab(tab); 46 | return gDevTools.showToolbox(target, toolId); 47 | } 48 | 49 | function getToolboxWhenReady(toolId) { 50 | let deferred = defer(); 51 | showToolbox(toolId).then(toolbox => { 52 | return deferred.resolve(toolbox); 53 | }); 54 | return deferred.promise; 55 | } 56 | 57 | function cancelEvent(event) { 58 | event.stopPropagation(); 59 | event.preventDefault(); 60 | } 61 | 62 | /** 63 | * This object represents a wrapper for start button node that is 64 | * used as an anchor for Notification Panel object (displayed when 65 | * the extension is installed for the first time). 66 | * 67 | * Note that passing directly a DOM node to Panel.show() method is 68 | * an unsupported feature that will be soon replaced. 69 | * See: https://bugzilla.mozilla.org/show_bug.cgi?id=878877 70 | */ 71 | var StartButtonAnchor = Class( 72 | /** @lends StartButtonAnchor */ 73 | { 74 | initialize: function(button) { 75 | this.node = button; 76 | } 77 | }); 78 | 79 | // getNodeView is used by the SDK panel to get the target anchor node. 80 | getNodeView.define(StartButtonAnchor, anchor => { 81 | return anchor.node; 82 | }); 83 | 84 | /** 85 | * This object represents a button that is automatically displayed 86 | * in the main Firefox toolbar after the extension is installed. 87 | * It serves as the entry point to the rest of the UI. 88 | */ 89 | var StartButton = 90 | /** @lends StartButton */ 91 | { 92 | // Initialization 93 | 94 | initialize: function() { 95 | // Create customizable button in browser toolbar. 96 | // Read more: 97 | // https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/CustomizableUI.jsm 98 | // https://blog.mozilla.org/addons/2014/03/06/australis-for-add-on-developers-2/ 99 | CustomizableUI.createWidget({ 100 | id: startButtonId, 101 | type: "custom", 102 | defaultArea: AREA_NAVBAR, 103 | allowedAreas: [AREA_PANEL, AREA_NAVBAR], 104 | onBuild: this.onBuild.bind(this) 105 | }); 106 | 107 | this.connectionListWindow = new ConnectionListWindow(); 108 | }, 109 | 110 | shutdown: function(/*reason*/) { 111 | CustomizableUI.destroyWidget(startButtonId); 112 | }, 113 | 114 | onBuild: function(doc) { 115 | Trace.sysout("startButton.onBuild;", doc); 116 | 117 | // Create a toolbar button with associated context menu. 118 | let button = new ToolbarButton({ 119 | document: doc, 120 | id: startButtonId, 121 | label: "rdpInspector.startButton.title", 122 | tooltiptext: "rdpInspector.startButton.tip", 123 | type: "menu-button", 124 | "class": "toolbarbutton-1 chromeclass-toolbar-additional", 125 | image: "chrome://rdpinspector/skin/icon16.png", 126 | items: this.getMenuItems.bind(this, doc.defaultView), 127 | command: this.onToggleRdpInspector.bind(this) 128 | }); 129 | 130 | return button.button; 131 | }, 132 | 133 | getAnchor: function(doc) { 134 | let startButton = this.getButton(doc); 135 | return new StartButtonAnchor(startButton); 136 | }, 137 | 138 | getButton: function(doc) { 139 | return doc.getElementById(startButtonId); 140 | }, 141 | 142 | // Menu Actions 143 | 144 | getMenuItems: function(/*win*/) { 145 | let items = []; 146 | 147 | items.push({ 148 | nol10n: true, 149 | label: Locale.$STR("rdpInspector.menu.options.label"), 150 | type: "menu", 151 | items: [ 152 | { 153 | nol10n: true, 154 | label: Locale.$STR("rdpInspector.menu.autoOpenOnWebIDEConnection.label"), 155 | tooltiptext: Locale.$STR("rdpInspector.menu.autoOpenOnWebIDEConnection.tip"), 156 | type: "checkbox", 157 | checked: prefs.webideConnectionsMonitor, 158 | command: this.onToggleAutoOpenOnWebideConnections.bind(this) 159 | }, 160 | { 161 | nol10n: true, 162 | label: Locale.$STR("rdpInspector.menu.packetCache.label"), 163 | tooltiptext: Locale.$STR("rdpInspector.menu.packetCache.tip"), 164 | type: "checkbox", 165 | checked: prefs.packetCacheEnabled, 166 | command: this.onTogglePacketCache.bind(this) 167 | }, 168 | { 169 | nol10n: true, 170 | label: Locale.$STR("rdpInspector.menu.showInlineDetails.label"), 171 | tooltiptext: Locale.$STR("rdpInspector.menu.showInlineDetails.tip"), 172 | type: "checkbox", 173 | checked: prefs.showInlineDetails, 174 | command: this.onToggleInlineDetails.bind(this) 175 | } 176 | ] 177 | }); 178 | 179 | items.push("-"); 180 | 181 | items.push({ 182 | nol10n: true, 183 | label: Locale.$STR("rdpInspector.menu.connectionList.label"), 184 | tooltiptext: Locale.$STR("rdpInspector.menu.connectionList.tip"), 185 | command: this.onConnectionList.bind(this) 186 | }); 187 | 188 | items.push("-"); 189 | 190 | items.push({ 191 | nol10n: true, 192 | label: Locale.$STR("rdpInspector.menu.visitHomePage.label"), 193 | tooltiptext: Locale.$STR("rdpInspector.menu.visitHomePage.tip"), 194 | command: this.onVisitHomePage.bind(this) 195 | }); 196 | 197 | items.push({ 198 | nol10n: true, 199 | label: Locale.$STR("rdpInspector.menu.reportIssue.label"), 200 | tooltiptext: Locale.$STR("rdpInspector.menu.reportIssue.tip"), 201 | command: this.onReportIssue.bind(this) 202 | }); 203 | 204 | items.push({ 205 | nol10n: true, 206 | label: Locale.$STR("rdpInspector.menu.group.label"), 207 | tooltiptext: Locale.$STR("rdpInspector.menu.group.tip"), 208 | command: this.onDiscussionGroup.bind(this) 209 | }); 210 | 211 | items.push("-"); 212 | 213 | items.push({ 214 | nol10n: true, 215 | label: Locale.$STR("rdpInspector.menu.about.label") + " " + self.version, 216 | tooltiptext: Locale.$STR("rdpInspector.menu.about.tip"), 217 | image: "chrome://rdpinspector/skin/logo_16x16.png", 218 | command: this.onAbout.bind(this) 219 | }); 220 | 221 | return items; 222 | }, 223 | 224 | onToggleRdpInspector: function() { 225 | Trace.sysout("startButton.onToggleRdpInspector;"); 226 | 227 | return getToolboxWhenReady().then(toolbox => { 228 | Dispatcher.emit("onToggleRDPInspector", [{ toolbox }]); 229 | 230 | return toolbox; 231 | }); 232 | }, 233 | 234 | // Commands 235 | onConnectionList: function(event) { 236 | cancelEvent(event); 237 | 238 | this.connectionListWindow.open(); 239 | }, 240 | 241 | onToggleAutoOpenOnWebideConnections: function(event) { 242 | cancelEvent(event); 243 | 244 | prefs.autoOpenOnWebIDEConnection = !prefs.autoOpenOnWebIDEConnection; 245 | }, 246 | 247 | onTogglePacketCache: function(event) { 248 | cancelEvent(event); 249 | 250 | prefs.packetCacheEnabled = !prefs.packetCacheEnabled; 251 | }, 252 | 253 | onToggleInlineDetails: function(event) { 254 | cancelEvent(event); 255 | 256 | prefs.showInlineDetails = !prefs.showInlineDetails; 257 | }, 258 | 259 | onAbout: function(event) { 260 | cancelEvent(event); 261 | 262 | AddonManager.getAddonByID(self.id, function (addon) { 263 | let browser = getMostRecentBrowserWindow(); 264 | browser.openDialog("chrome://mozapps/content/extensions/about.xul", "", 265 | "chrome,centerscreen,modal", addon); 266 | }); 267 | }, 268 | 269 | onVisitHomePage: function(event) { 270 | cancelEvent(event); 271 | 272 | let browser = getMostRecentBrowserWindow(); 273 | openTab(browser, options.manifest.homepage); 274 | }, 275 | 276 | onReportIssue: function(event) { 277 | cancelEvent(event); 278 | 279 | let browser = getMostRecentBrowserWindow(); 280 | openTab(browser, options.manifest.bugs.url); 281 | }, 282 | 283 | onDiscussionGroup: function(event) { 284 | cancelEvent(event); 285 | 286 | let browser = getMostRecentBrowserWindow(); 287 | openTab(browser, options.manifest.forum); 288 | } 289 | }; 290 | 291 | // Registration 292 | Dispatcher.register(StartButton); 293 | 294 | // Exports from this module 295 | exports.StartButton = StartButton; 296 | --------------------------------------------------------------------------------