├── test
├── folder
│ ├── index.html
│ └── redirect.html
├── http
│ └── index.html
├── headful.js
├── test.js
├── color.spec.js
└── app.spec.js
├── .eslintignore
├── examples
├── systeminfo
│ ├── .gitignore
│ ├── app_icon.png
│ ├── www
│ │ ├── favicon.ico
│ │ ├── fonts
│ │ │ └── roboto-v18-latin-regular.woff2
│ │ └── index.html
│ ├── README.md
│ ├── package.json
│ ├── main.js
│ ├── test.js
│ └── app.js
├── terminal
│ ├── .gitignore
│ ├── app_icon.png
│ ├── www
│ │ ├── favicon.ico
│ │ └── index.html
│ ├── README.md
│ ├── package.json
│ ├── worker.js
│ └── main.js
├── photobooth
│ ├── app_icon.png
│ ├── www
│ │ ├── favicon.ico
│ │ ├── camera.svg
│ │ └── index.html
│ ├── README.md
│ ├── package.json
│ └── main.js
└── windows
│ ├── README.md
│ ├── package.json
│ ├── main.html
│ └── main.js
├── .gitignore
├── .npmignore
├── index.js
├── rpc
├── index.js
├── rpc_process.js
├── rpc.md
├── test.js
└── rpc.js
├── package.json
├── lib
├── features
│ ├── file_info.js
│ └── shortcuts.js
├── http_request.js
├── find_chrome.js
├── color.js
└── carlo.js
├── CONTRIBUTING.md
├── .eslintrc.js
├── README.md
├── LICENSE
└── API.md
/test/folder/index.html:
--------------------------------------------------------------------------------
1 | hello file
--------------------------------------------------------------------------------
/test/http/index.html:
--------------------------------------------------------------------------------
1 | hello http
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | examples
2 | node_modules
3 |
--------------------------------------------------------------------------------
/test/folder/redirect.html:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/examples/systeminfo/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | package-lock.json
3 | .profile
4 | .DS_Store
5 | .vscode
6 | .idea
--------------------------------------------------------------------------------
/examples/terminal/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | package-lock.json
3 | .profile
4 | .DS_Store
5 | .vscode
6 | .idea
--------------------------------------------------------------------------------
/examples/terminal/app_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoogleChromeLabs/carlo/master/examples/terminal/app_icon.png
--------------------------------------------------------------------------------
/examples/photobooth/app_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoogleChromeLabs/carlo/master/examples/photobooth/app_icon.png
--------------------------------------------------------------------------------
/examples/systeminfo/app_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoogleChromeLabs/carlo/master/examples/systeminfo/app_icon.png
--------------------------------------------------------------------------------
/examples/terminal/www/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoogleChromeLabs/carlo/master/examples/terminal/www/favicon.ico
--------------------------------------------------------------------------------
/examples/photobooth/www/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoogleChromeLabs/carlo/master/examples/photobooth/www/favicon.ico
--------------------------------------------------------------------------------
/examples/systeminfo/www/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoogleChromeLabs/carlo/master/examples/systeminfo/www/favicon.ico
--------------------------------------------------------------------------------
/examples/systeminfo/www/fonts/roboto-v18-latin-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoogleChromeLabs/carlo/master/examples/systeminfo/www/fonts/roboto-v18-latin-regular.woff2
--------------------------------------------------------------------------------
/examples/windows/README.md:
--------------------------------------------------------------------------------
1 | ### Usage
2 |
3 | Install dependencies
4 |
5 | ```bash
6 | npm i
7 | ```
8 |
9 | Run application
10 |
11 | ```bash
12 | npm start
13 | ```
14 |
--------------------------------------------------------------------------------
/examples/terminal/README.md:
--------------------------------------------------------------------------------
1 | ### Usage
2 |
3 | Install dependencies
4 |
5 | ```bash
6 | npm i
7 | ```
8 |
9 | Run application
10 |
11 | ```bash
12 | npm start
13 | ```
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .eslintrc.js
3 | .eslintignore
4 | .profile
5 | .vscode
6 | node_modules
7 | package-lock.json
8 | rpc/node_modules
9 | rpc/package-lock.json
10 | lib/.local-data
--------------------------------------------------------------------------------
/examples/systeminfo/README.md:
--------------------------------------------------------------------------------
1 | ### Usage
2 |
3 | Install dependencies
4 |
5 | ```bash
6 | npm i
7 | ```
8 |
9 | Run application
10 |
11 | ```bash
12 | npm start
13 | ```
14 |
15 | Optionally package as executable
16 |
17 | ```bash
18 | pkg package.json
19 | ```
20 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # repeats from .gitignore
2 | .DS_Store
3 | .eslintrc.js
4 | .eslintignore
5 | .profile
6 | .vscode
7 | node_modules
8 | package-lock.json
9 | rpc/node_modules
10 | rpc/package-lock.json
11 | lib/.local-data
12 |
13 | .npmignore
14 | examples
15 | CONTRIBUTING.md
16 | API.md
17 | rpc/test.js
18 | test
19 |
--------------------------------------------------------------------------------
/examples/photobooth/README.md:
--------------------------------------------------------------------------------
1 | ### Usage
2 |
3 | > This example requires Chrome 72 (Chrome Canary) to function.
4 |
5 | Install dependencies
6 |
7 | ```bash
8 | npm i
9 | ```
10 |
11 | Run application
12 |
13 | ```bash
14 | npm start
15 | ```
16 |
17 | Optionally package as executable
18 |
19 | ```bash
20 | pkg package.json
21 | ```
22 |
--------------------------------------------------------------------------------
/examples/photobooth/www/camera.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/examples/windows/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "windows-app",
3 | "version": "0.9.0",
4 | "description": "Multiple windows example",
5 | "main": "main.js",
6 | "scripts": {
7 | "start": "node main.js"
8 | },
9 | "bin": {
10 | "windows-app": "./main.js"
11 | },
12 | "keywords": [],
13 | "author": "The Chromium Authors",
14 | "license": "Apache-2.0",
15 | "dependencies": {
16 | "carlo": "^0.9.0"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/examples/terminal/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "xterm-app",
3 | "version": "0.9.0",
4 | "description": "Terminal example",
5 | "main": "main.js",
6 | "scripts": {
7 | "start": "node main.js"
8 | },
9 | "bin": {
10 | "xterm-app": "./main.js"
11 | },
12 | "keywords": [],
13 | "author": "The Chromium Authors",
14 | "license": "Apache-2.0",
15 | "dependencies": {
16 | "carlo": "^0.9.0",
17 | "ndb-node-pty-prebuilt": "^0.8.0",
18 | "xterm": "~3.8.1"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/examples/photobooth/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "photobooth-app",
3 | "version": "0.9.0",
4 | "description": "Photo Booth App",
5 | "main": "main.js",
6 | "scripts": {
7 | "bundle": "pkg package.json",
8 | "start": "node main.js"
9 | },
10 | "bin": {
11 | "photobooth-app": "./main.js"
12 | },
13 | "pkg": {
14 | "scripts": "*.js",
15 | "assets": "www/**/*"
16 | },
17 | "keywords": [],
18 | "author": "The Chromium Authors",
19 | "license": "Apache-2.0",
20 | "dependencies": {
21 | "carlo": "^0.9.0",
22 | "systeminformation": "^3.45.9"
23 | },
24 | "devDependencies": {
25 | "pkg": "^4.3.4"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/examples/systeminfo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "systeminfo-app",
3 | "version": "0.9.0",
4 | "description": "System info example",
5 | "main": "main.js",
6 | "scripts": {
7 | "bundle": "pkg package.json",
8 | "start": "node main.js",
9 | "test": "node test.js"
10 | },
11 | "bin": {
12 | "systeminfo-app": "./main.js"
13 | },
14 | "pkg": {
15 | "scripts": "*.js",
16 | "assets": "www/**/*"
17 | },
18 | "keywords": [],
19 | "author": "The Chromium Authors",
20 | "license": "Apache-2.0",
21 | "dependencies": {
22 | "carlo": "^0.9.0",
23 | "systeminformation": "^3.45.9"
24 | },
25 | "devDependencies": {
26 | "pkg": "^4.3.4",
27 | "@pptr/testrunner": "^0.5.0"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the 'License');
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an 'AS IS' BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | 'use strict';
18 |
19 | module.exports = require('./lib/carlo');
20 |
--------------------------------------------------------------------------------
/examples/systeminfo/main.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018 Google Inc., PhantomJS Authors All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | 'use strict';
18 |
19 | require('./app.js').run();
20 |
--------------------------------------------------------------------------------
/rpc/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the 'License');
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an 'AS IS' BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | 'use strict';
18 |
19 | module.exports = {
20 | rpc: require('./rpc'),
21 | rpc_process: require('./rpc_process')
22 | };
23 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "carlo",
3 | "version": "0.9.46",
4 | "description": "Carlo is a framework for rendering Node data structures using Chrome browser.",
5 | "repository": "github:GoogleChromeLabs/carlo",
6 | "engines": {
7 | "node": ">=7.6.0"
8 | },
9 | "main": "index.js",
10 | "directories": {
11 | "lib": "lib"
12 | },
13 | "scripts": {
14 | "lint": "([ \"$CI\" = true ] && eslint --quiet -f codeframe . || eslint .)",
15 | "test": "node rpc/test.js && node test/test.js",
16 | "headful-test": "node test/headful.js"
17 | },
18 | "keywords": [],
19 | "author": "The Chromium Authors",
20 | "license": "Apache-2.0",
21 | "dependencies": {
22 | "debug": "^4.1.0",
23 | "puppeteer-core": "~1.12.0"
24 | },
25 | "devDependencies": {
26 | "eslint": "^5.8.0",
27 | "@pptr/testrunner": "^0.5.0",
28 | "@pptr/testserver": "^0.5.0"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/lib/features/file_info.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the 'License');
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an 'AS IS' BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | module.exports = function install(hostWindow) {
18 | let lastFileId = 0;
19 | self.carlo.fileInfo = async(file) => {
20 | const fileId = ++lastFileId;
21 | self.carlo.fileInfo.files_.set(fileId, file);
22 | const result = await hostWindow.fileInfo(`self.carlo.fileInfo.files_.get(${fileId})`);
23 | self.carlo.fileInfo.files_.delete(fileId);
24 | return result;
25 | };
26 |
27 | self.carlo.fileInfo.files_ = new Map();
28 | };
29 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to Contribute
2 |
3 | We'd love to accept your patches and contributions to this project. There are
4 | just a few small guidelines you need to follow.
5 |
6 | ## Contributor License Agreement
7 |
8 | Contributions to this project must be accompanied by a Contributor License
9 | Agreement. You (or your employer) retain the copyright to your contribution;
10 | this simply gives us permission to use and redistribute your contributions as
11 | part of the project. Head over to to see
12 | your current agreements on file or to sign a new one.
13 |
14 | You generally only need to submit a CLA once, so if you've already submitted one
15 | (even if it was for a different project), you probably don't need to do it
16 | again.
17 |
18 | ## Code reviews
19 |
20 | All submissions, including submissions by project members, require review. We
21 | use GitHub pull requests for this purpose. Consult
22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
23 | information on using pull requests.
24 |
25 | ## Community Guidelines
26 |
27 | This project follows [Google's Open Source Community
28 | Guidelines](https://opensource.google.com/conduct/).
29 |
--------------------------------------------------------------------------------
/examples/windows/main.html:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 | Main
19 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/rpc/rpc_process.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the 'License');
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an 'AS IS' BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | 'use strict';
18 |
19 | const child_process = require('child_process');
20 | const rpc = require('./rpc');
21 |
22 | async function spawn(fileName, ...args) {
23 | const child = child_process.fork(fileName, [], {
24 | detached: true, stdio: [0, 1, 2, 'ipc']
25 | });
26 |
27 | const transport = receivedFromChild => {
28 | child.on('message', receivedFromChild);
29 | return child.send.bind(child);
30 | };
31 | const { result } = await rpc.createWorld(transport, ...args);
32 | return result;
33 | }
34 |
35 | function init(initializer) {
36 | const transport = receivedFromParent => {
37 | process.on('message', receivedFromParent);
38 | return process.send.bind(process);
39 | };
40 | rpc.initWorld(transport, initializer);
41 | }
42 |
43 | module.exports = { spawn, init };
44 |
--------------------------------------------------------------------------------
/examples/terminal/worker.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | /**
4 | * Copyright 2018 Google Inc., PhantomJS Authors All rights reserved.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | 'use strict';
20 |
21 | const EventEmitter = require('events');
22 | const os = require('os');
23 | const pty = require('ndb-node-pty-prebuilt');
24 | const { rpc, rpc_process } = require('carlo/rpc');
25 |
26 | class Terminal extends EventEmitter {
27 | constructor() {
28 | super();
29 | const shell = os.platform() === 'win32' ? 'powershell.exe' : 'bash';
30 | this.term_ = pty.spawn(shell, [], {
31 | name: 'xterm-color',
32 | cwd: process.env.PWD,
33 | env: process.env
34 | });
35 | this.term_.on('data', data => this.emit('data', data));
36 | }
37 |
38 | on(event, func) {
39 | // EventEmitter returns heavy object that we don't want to
40 | // send over the wire.
41 | super.on(event, func);
42 | }
43 |
44 | resize(cols, rows) {
45 | this.term_.resize(cols, rows);
46 | }
47 |
48 | write(data) {
49 | this.term_.write(data);
50 | }
51 |
52 | dispose() {
53 | process.kill(this._term.pid);
54 | }
55 | }
56 |
57 | rpc_process.init(() => rpc.handle(new Terminal));
58 |
--------------------------------------------------------------------------------
/examples/systeminfo/test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the 'License');
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an 'AS IS' BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | const {TestRunner, Reporter, Matchers} = require('@pptr/testrunner');
18 |
19 | require('carlo').enterTestMode();
20 | const { run } = require('./app');
21 |
22 | // Runner holds and runs all the tests
23 | const runner = new TestRunner({
24 | parallel: 1, // run 2 parallel threads
25 | timeout: 3000, // setup timeout of 1 second per test
26 | });
27 | // Simple expect-like matchers
28 | const {expect} = new Matchers();
29 |
30 | // Extract jasmine-like DSL into the global namespace
31 | const {describe, xdescribe, fdescribe} = runner;
32 | const {it, fit, xit} = runner;
33 | const {beforeAll, beforeEach, afterAll, afterEach} = runner;
34 |
35 | describe('test', () => {
36 | it('test columns', async(state, test) => {
37 | const app = await run();
38 | const page = app.mainWindow().pageForTest();
39 | await page.waitForSelector('.header');
40 | const columns = await page.$$eval('.header', nodes => nodes.map(n => n.textContent));
41 | expect(columns.sort().join(',')).toBe('battery,cpu,osInfo');
42 | });
43 | });
44 |
45 | // Reporter subscribes to TestRunner events and displays information in terminal
46 | new Reporter(runner);
47 |
48 | // Run all tests.
49 | runner.run();
50 |
--------------------------------------------------------------------------------
/examples/windows/main.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | /**
4 | * Copyright 2018 Google Inc., PhantomJS Authors All rights reserved.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | 'use strict';
20 |
21 | const carlo = require('carlo');
22 | const { rpc } = require('carlo/rpc');
23 |
24 | class Backend {
25 | constructor(app) {
26 | this.app_ = app;
27 | this.windows_ = new Map();
28 | }
29 |
30 | showMyWindow(url) {
31 | let windowPromise = this.windows_.get(url);
32 | if (!windowPromise) {
33 | windowPromise = this.createWindow_(url);
34 | this.windows_.set(url, windowPromise);
35 | }
36 | windowPromise.then(w => w.bringToFront());
37 | }
38 |
39 | async createWindow_(url) {
40 | const window = await this.app_.createWindow({width: 800, height: 600, top: 200, left: 10});
41 | window.on('close', () => this.windows_.delete(url));
42 | window.load(url);
43 | return window;
44 | }
45 | }
46 |
47 | (async() => {
48 | const app = await carlo.launch(
49 | {title: 'Main', width: 300, height: 100, top: 10, left: 10 });
50 | app.on('exit', () => process.exit());
51 | const mainWindow = app.mainWindow();
52 | mainWindow.on('close', () => process.exit());
53 | mainWindow.serveFolder(__dirname);
54 | mainWindow.load('main.html', rpc.handle(new Backend(app)));
55 | })();
56 |
--------------------------------------------------------------------------------
/test/headful.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the 'License');
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an 'AS IS' BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | const {TestRunner, Reporter, Matchers} = require('@pptr/testrunner');
18 |
19 | const path = require('path');
20 | const carlo = require('../lib/carlo');
21 |
22 | // Runner holds and runs all the tests
23 | const testRunner = new TestRunner({
24 | parallel: 1, // run 2 parallel threads
25 | timeout: 3000, // setup timeout of 1 second per test
26 | });
27 | const {expect} = new Matchers();
28 | const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
29 | const {describe, xdescribe, fdescribe} = testRunner;
30 | const {it, fit, xit} = testRunner;
31 |
32 | describe('app reuse', () => {
33 | fit('load returns value', async() => {
34 | app = await carlo.launch();
35 | let callback;
36 | const windowPromise = new Promise(f => callback = f);
37 | app.on('window', callback);
38 |
39 | try {
40 | await carlo.launch({paramsForReuse: {val: 42}});
41 | expect(false).toBeTruthy();
42 | } catch (e) {
43 | expect(e.toString()).toContain('already running');
44 | }
45 |
46 | const window = await windowPromise;
47 | expect(JSON.stringify(window.paramsForReuse())).toBe('{"val":42}');
48 | });
49 | });
50 |
51 | // Reporter subscribes to TestRunner events and displays information in terminal
52 | new Reporter(testRunner);
53 |
54 | // Run all tests.
55 | testRunner.run();
56 |
--------------------------------------------------------------------------------
/examples/photobooth/main.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | 'use strict';
18 |
19 | const carlo = require('carlo');
20 | const fs = require('fs');
21 | const path = require('path');
22 | const os = require('os');
23 |
24 | (async () => {
25 | let app;
26 | try {
27 | app = await carlo.launch(
28 | {
29 | bgcolor: '#e6e8ec',
30 | width: 800,
31 | height: 648 + 24,
32 | icon: path.join(__dirname, '/app_icon.png'),
33 | channel: ['canary', 'stable'],
34 | localDataDir: path.join(os.homedir(), '.carlophotobooth'),
35 | });
36 | } catch(e) {
37 | // New window is opened in the running instance.
38 | console.log('Reusing the running instance');
39 | return;
40 | }
41 | app.on('exit', () => process.exit());
42 | // New windows are opened when this app is started again from command line.
43 | app.on('window', window => window.load('index.html'));
44 | app.serveFolder(path.join(__dirname, '/www'));
45 | await app.exposeFunction('saveImage', saveImage);
46 | await app.load('index.html');
47 | })();
48 |
49 | function saveImage(base64) {
50 | var buffer = Buffer.from(base64, 'base64')
51 | if (!fs.existsSync('pictures'))
52 | fs.mkdirSync('pictures');
53 | const fileName = path.join('pictures', new Date().toISOString().replace(/:/g,'-') + '.jpeg');
54 | fs.writeFileSync(fileName, buffer);
55 | }
56 |
--------------------------------------------------------------------------------
/examples/terminal/www/index.html:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 | Terminal App
21 |
27 |
28 |
29 |
30 |
56 |
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/examples/systeminfo/app.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018 Google Inc., PhantomJS Authors All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | 'use strict';
18 |
19 | const carlo = require('carlo');
20 | const os = require('os');
21 | const path = require('path');
22 | const si = require('systeminformation');
23 |
24 | async function run() {
25 | let app;
26 | try {
27 | app = await carlo.launch(
28 | {
29 | bgcolor: '#2b2e3b',
30 | title: 'Systeminfo App',
31 | width: 1000,
32 | height: 500,
33 | channel: ['canary', 'stable'],
34 | icon: path.join(__dirname, '/app_icon.png'),
35 | args: process.env.DEV === 'true' ? ['--auto-open-devtools-for-tabs'] : [],
36 | localDataDir: path.join(os.homedir(), '.carlosysteminfo'),
37 | });
38 | } catch(e) {
39 | // New window is opened in the running instance.
40 | console.log('Reusing the running instance');
41 | return;
42 | }
43 | app.on('exit', () => process.exit());
44 | // New windows are opened when this app is started again from command line.
45 | app.on('window', window => window.load('index.html'));
46 | app.serveFolder(path.join(__dirname, 'www'));
47 | await app.exposeFunction('systeminfo', systeminfo);
48 | await app.load('index.html');
49 | return app;
50 | }
51 |
52 | async function systeminfo() {
53 | const info = {};
54 | await Promise.all([
55 | si.battery().then(r => info.battery = r),
56 | si.cpu().then(r => info.cpu = r),
57 | si.osInfo().then(r => info.osInfo = r),
58 | ]);
59 | return info;
60 | }
61 |
62 | module.exports = { run };
63 |
--------------------------------------------------------------------------------
/examples/terminal/main.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | /**
4 | * Copyright 2018 Google Inc., PhantomJS Authors All rights reserved.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | 'use strict';
20 |
21 | const carlo = require('carlo');
22 | const path = require('path');
23 | const { rpc, rpc_process } = require('carlo/rpc');
24 |
25 | class TerminalApp {
26 | constructor() {
27 | this.lastTop_ = 50;
28 | this.lastLeft_ = 50;
29 | this.launch_();
30 | this.handle_ = rpc.handle(this);
31 | }
32 |
33 | async launch_() {
34 | try {
35 | this.app_ = await carlo.launch({
36 | bgcolor: '#2b2e3b',
37 | title: 'Terminal App',
38 | width: 800,
39 | height: 800,
40 | channel: ['canary', 'stable'],
41 | icon: path.join(__dirname, '/app_icon.png'),
42 | top: this.lastTop_,
43 | left: this.lastLeft_ });
44 | } catch (e) {
45 | console.log('Reusing the running instance');
46 | return;
47 | }
48 | this.app_.on('exit', () => process.exit());
49 | this.app_.serveFolder(path.join(__dirname, 'www'));
50 | this.app_.serveFolder(path.join(__dirname, 'node_modules'), 'node_modules');
51 | this.app_.on('window', win => this.initUI_(win));
52 | this.initUI_(this.app_.mainWindow());
53 | }
54 |
55 | async newWindow() {
56 | this.lastTop_ = (this.lastTop_ + 50) % 200;
57 | this.lastLeft_ += 50;
58 | const options = { top: this.lastTop_, left: this.lastLeft_ };
59 | this.app_.createWindow(options);
60 | }
61 |
62 | async initUI_(win) {
63 | const term = await rpc_process.spawn('worker.js');
64 | win.load('index.html', this.handle_, term);
65 | }
66 | }
67 |
68 | new TerminalApp();
69 |
--------------------------------------------------------------------------------
/test/test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the 'License');
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an 'AS IS' BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | const {TestRunner, Reporter, Matchers} = require('@pptr/testrunner');
18 | const {TestServer} = require('@pptr/testserver');
19 |
20 | const path = require('path');
21 | const carlo = require('../lib/carlo');
22 | carlo.enterTestMode();
23 |
24 | // Runner holds and runs all the tests
25 | const testRunner = new TestRunner({
26 | parallel: 1, // run 2 parallel threads
27 | timeout: 3000, // setup timeout of 1 second per test
28 | });
29 | const {expect} = new Matchers();
30 | const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
31 |
32 | beforeAll(async state => {
33 | const assetsPath = path.join(__dirname, 'http');
34 |
35 | const port = 8907 + state.parallelIndex * 2;
36 | state.server = await TestServer.create(assetsPath, port);
37 | state.server.PORT = port;
38 | state.server.PREFIX = `http://localhost:${port}`;
39 |
40 | const httpsPort = port + 1;
41 | state.httpsServer = await TestServer.createHTTPS(assetsPath, httpsPort);
42 | state.httpsServer.PORT = httpsPort;
43 | state.httpsServer.PREFIX = `https://localhost:${httpsPort}`;
44 | });
45 |
46 | afterAll(async({server, httpsServer}) => {
47 | await Promise.all([
48 | server.stop(),
49 | httpsServer.stop(),
50 | ]);
51 | });
52 |
53 | beforeEach(async({server, httpsServer}) => {
54 | server.reset();
55 | httpsServer.reset();
56 | });
57 |
58 | require('./app.spec.js').addTests({testRunner, expect});
59 | require('./color.spec.js').addTests({testRunner, expect});
60 |
61 | // Reporter subscribes to TestRunner events and displays information in terminal
62 | new Reporter(testRunner);
63 |
64 | // Run all tests.
65 | testRunner.run();
66 |
--------------------------------------------------------------------------------
/lib/features/shortcuts.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the 'License');
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an 'AS IS' BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | module.exports = function install(hostWindow) {
18 | const ctrlOrCmdCodes = new Set(
19 | ['KeyD', 'KeyE', 'KeyD', 'KeyG', 'KeyN', 'KeyO', 'KeyP', 'KeyQ', 'KeyR', 'KeyS',
20 | 'KeyT', 'KeyW', 'KeyY', 'Tab', 'PageUp', 'PageDown', 'F4']);
21 | const cmdCodes = new Set(['BracketLeft', 'BracketRight', 'Comma']);
22 | const cmdOptionCodes = new Set(['ArrowLeft', 'ArrowRight', 'KeyB']);
23 | const ctrlShiftCodes = new Set(['KeyQ', 'KeyW']);
24 | const altCodes = new Set(['Home', 'ArrowLeft', 'ArrowRight', 'F4']);
25 |
26 | function preventDefaultShortcuts(event) {
27 | let prevent = false;
28 | if (navigator.userAgent.match(/Mac OS X/)) {
29 | if (event.metaKey) {
30 | if (event.keyCode > 48 && event.keyCode <= 57) // 1-9
31 | prevent = true;
32 | if (ctrlOrCmdCodes.has(event.code) || cmdCodes.has(event.code))
33 | prevent = true;
34 | if (event.shiftKey && cmdOptionCodes.has(event.code))
35 | prevent = true;
36 | if (event.code === 'ArrowLeft' || event.code === 'ArrowRight') {
37 | if (!event.contentEditable && event.target.nodeName !== 'INPUT' && event.target.nodeName !== 'TEXTAREA')
38 | prevent = true;
39 | }
40 | }
41 | } else {
42 | if (event.code === 'F4')
43 | prevent = true;
44 | if (event.ctrlKey) {
45 | if (event.keyCode > 48 && event.keyCode <= 57) // 1-9
46 | prevent = true;
47 | if (ctrlOrCmdCodes.has(event.code))
48 | prevent = true;
49 | if (event.shiftKey && ctrlShiftCodes.has(event.code))
50 | prevent = true;
51 | }
52 | if (event.altKey && altCodes.has(event.code))
53 | prevent = true;
54 | }
55 |
56 | if (prevent)
57 | event.preventDefault();
58 | }
59 |
60 | document.addEventListener('keydown', preventDefaultShortcuts, false);
61 | document.addEventListener('keydown', event => {
62 | if ((event.key === 'q' || event.key === 'Q') && (event.metaKey || event.ctrlKey)) {
63 | hostWindow.closeBrowser();
64 | event.preventDefault();
65 | }
66 | });
67 | };
68 |
--------------------------------------------------------------------------------
/examples/photobooth/www/index.html:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 | PhotoBooth App
19 |
72 |
73 |
93 |
94 |
95 |
96 |
97 |
100 |
101 |
102 |