Merge pull request #610 from atom/devtools-extension
Add support for chrome devtools extension
This commit is contained in:
commit
45228493eb
17 changed files with 285 additions and 23 deletions
4
atom.gyp
4
atom.gyp
|
@ -28,6 +28,7 @@
|
|||
'atom/browser/api/lib/protocol.coffee',
|
||||
'atom/browser/api/lib/tray.coffee',
|
||||
'atom/browser/api/lib/web-contents.coffee',
|
||||
'atom/browser/lib/chrome-extension.coffee',
|
||||
'atom/browser/lib/init.coffee',
|
||||
'atom/browser/lib/objects-registry.coffee',
|
||||
'atom/browser/lib/rpc-server.coffee',
|
||||
|
@ -38,6 +39,7 @@
|
|||
'atom/common/api/lib/screen.coffee',
|
||||
'atom/common/api/lib/shell.coffee',
|
||||
'atom/common/lib/init.coffee',
|
||||
'atom/renderer/lib/chrome-api.coffee',
|
||||
'atom/renderer/lib/init.coffee',
|
||||
'atom/renderer/lib/inspector.coffee',
|
||||
'atom/renderer/lib/override.coffee',
|
||||
|
@ -46,6 +48,8 @@
|
|||
'atom/renderer/api/lib/web-view.coffee',
|
||||
],
|
||||
'lib_sources': [
|
||||
'atom/app/atom_content_client.cc',
|
||||
'atom/app/atom_content_client.h',
|
||||
'atom/app/atom_main_delegate.cc',
|
||||
'atom/app/atom_main_delegate.h',
|
||||
'atom/app/atom_main_delegate_mac.mm',
|
||||
|
|
24
atom/app/atom_content_client.cc
Normal file
24
atom/app/atom_content_client.cc
Normal file
|
@ -0,0 +1,24 @@
|
|||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/app/atom_content_client.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace atom {
|
||||
|
||||
AtomContentClient::AtomContentClient() {
|
||||
}
|
||||
|
||||
AtomContentClient::~AtomContentClient() {
|
||||
}
|
||||
|
||||
void AtomContentClient::AddAdditionalSchemes(
|
||||
std::vector<std::string>* standard_schemes,
|
||||
std::vector<std::string>* savable_schemes) {
|
||||
standard_schemes->push_back("chrome-extension");
|
||||
}
|
||||
|
||||
} // namespace atom
|
32
atom/app/atom_content_client.h
Normal file
32
atom/app/atom_content_client.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_APP_ATOM_CONTENT_CLIENT_H_
|
||||
#define ATOM_APP_ATOM_CONTENT_CLIENT_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "brightray/common/content_client.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
class AtomContentClient : public brightray::ContentClient {
|
||||
public:
|
||||
AtomContentClient();
|
||||
virtual ~AtomContentClient();
|
||||
|
||||
protected:
|
||||
// content::ContentClient:
|
||||
virtual void AddAdditionalSchemes(
|
||||
std::vector<std::string>* standard_schemes,
|
||||
std::vector<std::string>* savable_schemes) OVERRIDE;
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(AtomContentClient);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_APP_ATOM_CONTENT_CLIENT_H_
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <string>
|
||||
|
||||
#include "atom/app/atom_content_client.h"
|
||||
#include "atom/browser/atom_browser_client.h"
|
||||
#include "atom/renderer/atom_renderer_client.h"
|
||||
#include "base/command_line.h"
|
||||
|
@ -22,18 +23,6 @@ AtomMainDelegate::AtomMainDelegate() {
|
|||
AtomMainDelegate::~AtomMainDelegate() {
|
||||
}
|
||||
|
||||
void AtomMainDelegate::AddDataPackFromPath(
|
||||
ui::ResourceBundle* bundle, const base::FilePath& pak_dir) {
|
||||
#if defined(OS_WIN)
|
||||
bundle->AddDataPackFromPath(
|
||||
pak_dir.Append(FILE_PATH_LITERAL("ui_resources_200_percent.pak")),
|
||||
ui::SCALE_FACTOR_200P);
|
||||
bundle->AddDataPackFromPath(
|
||||
pak_dir.Append(FILE_PATH_LITERAL("webkit_resources_200_percent.pak")),
|
||||
ui::SCALE_FACTOR_200P);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool AtomMainDelegate::BasicStartupComplete(int* exit_code) {
|
||||
// Disable logging out to debug.log on Windows
|
||||
#if defined(OS_WIN)
|
||||
|
@ -99,4 +88,20 @@ content::ContentRendererClient*
|
|||
return renderer_client_.get();
|
||||
}
|
||||
|
||||
scoped_ptr<brightray::ContentClient> AtomMainDelegate::CreateContentClient() {
|
||||
return scoped_ptr<brightray::ContentClient>(new AtomContentClient).Pass();
|
||||
}
|
||||
|
||||
void AtomMainDelegate::AddDataPackFromPath(
|
||||
ui::ResourceBundle* bundle, const base::FilePath& pak_dir) {
|
||||
#if defined(OS_WIN)
|
||||
bundle->AddDataPackFromPath(
|
||||
pak_dir.Append(FILE_PATH_LITERAL("ui_resources_200_percent.pak")),
|
||||
ui::SCALE_FACTOR_200P);
|
||||
bundle->AddDataPackFromPath(
|
||||
pak_dir.Append(FILE_PATH_LITERAL("webkit_resources_200_percent.pak")),
|
||||
ui::SCALE_FACTOR_200P);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace atom
|
||||
|
|
|
@ -16,24 +16,23 @@ class AtomMainDelegate : public brightray::MainDelegate {
|
|||
~AtomMainDelegate();
|
||||
|
||||
protected:
|
||||
// brightray::MainDelegate:
|
||||
virtual void AddDataPackFromPath(
|
||||
ui::ResourceBundle* bundle, const base::FilePath& pak_dir) OVERRIDE;
|
||||
|
||||
// content::ContentMainDelegate:
|
||||
virtual bool BasicStartupComplete(int* exit_code) OVERRIDE;
|
||||
virtual void PreSandboxStartup() OVERRIDE;
|
||||
virtual content::ContentBrowserClient* CreateContentBrowserClient() OVERRIDE;
|
||||
virtual content::ContentRendererClient*
|
||||
CreateContentRendererClient() OVERRIDE;
|
||||
|
||||
// brightray::MainDelegate:
|
||||
virtual scoped_ptr<brightray::ContentClient> CreateContentClient() OVERRIDE;
|
||||
virtual void AddDataPackFromPath(
|
||||
ui::ResourceBundle* bundle, const base::FilePath& pak_dir) OVERRIDE;
|
||||
#if defined(OS_MACOSX)
|
||||
virtual void OverrideChildProcessPath() OVERRIDE;
|
||||
virtual void OverrideFrameworkBundlePath() OVERRIDE;
|
||||
#endif
|
||||
|
||||
private:
|
||||
virtual content::ContentBrowserClient* CreateContentBrowserClient() OVERRIDE;
|
||||
virtual content::ContentRendererClient*
|
||||
CreateContentRendererClient() OVERRIDE;
|
||||
|
||||
brightray::ContentClient content_client_;
|
||||
scoped_ptr<content::ContentBrowserClient> browser_client_;
|
||||
scoped_ptr<content::ContentRendererClient> renderer_client_;
|
||||
|
|
|
@ -35,6 +35,10 @@ BrowserWindow::openDevTools = ->
|
|||
@devToolsWebContents = @getDevToolsWebContents()
|
||||
@devToolsWebContents.once 'destroyed', => @devToolsWebContents = null
|
||||
|
||||
# Emit devtools events.
|
||||
@devToolsWebContents.once 'did-finish-load', => @emit 'devtools-opened'
|
||||
@devToolsWebContents.once 'destroyed', => @emit 'devtools-closed'
|
||||
|
||||
BrowserWindow::toggleDevTools = ->
|
||||
if @isDevToolsOpened() then @closeDevTools() else @openDevTools()
|
||||
|
||||
|
|
|
@ -85,6 +85,12 @@ void AtomBrowserClient::OverrideWebkitPrefs(
|
|||
prefs->allow_displaying_insecure_content = true;
|
||||
prefs->allow_running_insecure_content = true;
|
||||
|
||||
// Turn off web security for devtools.
|
||||
if (url.SchemeIs("chrome-devtools")) {
|
||||
prefs->web_security_enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
NativeWindow* window = NativeWindow::FromRenderView(
|
||||
render_view_host->GetProcess()->GetID(),
|
||||
render_view_host->GetRoutingID());
|
||||
|
|
88
atom/browser/lib/chrome-extension.coffee
Normal file
88
atom/browser/lib/chrome-extension.coffee
Normal file
|
@ -0,0 +1,88 @@
|
|||
app = require 'app'
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
url = require 'url'
|
||||
|
||||
# Mapping between hostname and file path.
|
||||
hostPathMap = {}
|
||||
hostPathMapNextKey = 0
|
||||
|
||||
getHostForPath = (path) ->
|
||||
key = "extension-#{++hostPathMapNextKey}"
|
||||
hostPathMap[key] = path
|
||||
key
|
||||
|
||||
getPathForHost = (host) ->
|
||||
hostPathMap[host]
|
||||
|
||||
# Cache extensionInfo.
|
||||
extensionInfoMap = {}
|
||||
|
||||
getExtensionInfoFromPath = (srcDirectory) ->
|
||||
manifest = JSON.parse fs.readFileSync(path.join(srcDirectory, 'manifest.json'))
|
||||
unless extensionInfoMap[manifest.name]?
|
||||
# We can not use 'file://' directly because all resources in the extension
|
||||
# will be treated as relative to the root in Chrome.
|
||||
page = url.format
|
||||
protocol: 'chrome-extension'
|
||||
slashes: true
|
||||
hostname: getHostForPath srcDirectory
|
||||
pathname: manifest.devtools_page
|
||||
extensionInfoMap[manifest.name] =
|
||||
startPage: page
|
||||
name: manifest.name
|
||||
srcDirectory: srcDirectory
|
||||
extensionInfoMap[manifest.name]
|
||||
|
||||
# Load persistented extensions.
|
||||
loadedExtensionsPath = path.join app.getDataPath(), 'DevTools Extensions'
|
||||
|
||||
try
|
||||
loadedExtensions = JSON.parse fs.readFileSync(loadedExtensionsPath)
|
||||
loadedExtensions = [] unless Array.isArray loadedExtensions
|
||||
# Preheat the extensionInfo cache.
|
||||
getExtensionInfoFromPath srcDirectory for srcDirectory in loadedExtensions
|
||||
catch e
|
||||
|
||||
# Persistent loaded extensions.
|
||||
app.on 'will-quit', ->
|
||||
try
|
||||
loadedExtensions = Object.keys(extensionInfoMap).map (key) -> extensionInfoMap[key].srcDirectory
|
||||
try
|
||||
fs.mkdirSync path.dirname(loadedExtensionsPath)
|
||||
catch e
|
||||
fs.writeFileSync loadedExtensionsPath, JSON.stringify(loadedExtensions)
|
||||
catch e
|
||||
|
||||
# We can not use protocol or BrowserWindow until app is ready.
|
||||
app.once 'ready', ->
|
||||
protocol = require 'protocol'
|
||||
BrowserWindow = require 'browser-window'
|
||||
|
||||
# The chrome-extension: can map a extension URL request to real file path.
|
||||
protocol.registerProtocol 'chrome-extension', (request) ->
|
||||
parsed = url.parse request.url
|
||||
return unless parsed.hostname and parsed.path?
|
||||
return unless /extension-\d+/.test parsed.hostname
|
||||
|
||||
directory = getPathForHost parsed.hostname
|
||||
return unless directory?
|
||||
return new protocol.RequestFileJob(path.join(directory, parsed.path))
|
||||
|
||||
BrowserWindow::_loadDevToolsExtensions = (extensionInfoArray) ->
|
||||
@devToolsWebContents?.executeJavaScript "WebInspector.addExtensions(#{JSON.stringify(extensionInfoArray)});"
|
||||
|
||||
BrowserWindow.addDevToolsExtension = (srcDirectory) ->
|
||||
extensionInfo = getExtensionInfoFromPath srcDirectory
|
||||
window._loadDevToolsExtensions [extensionInfo] for window in BrowserWindow.getAllWindows()
|
||||
extensionInfo.name
|
||||
|
||||
BrowserWindow.removeDevToolsExtension = (name) ->
|
||||
delete extensionInfoMap[name]
|
||||
|
||||
# Load persistented extensions when devtools is opened.
|
||||
init = BrowserWindow::_init
|
||||
BrowserWindow::_init = ->
|
||||
init.call this
|
||||
@on 'devtools-opened', ->
|
||||
@_loadDevToolsExtensions Object.keys(extensionInfoMap).map (key) -> extensionInfoMap[key]
|
|
@ -77,5 +77,8 @@ setImmediate ->
|
|||
else if packageJson.name?
|
||||
app.setName packageJson.name
|
||||
|
||||
# Load the chrome extension support.
|
||||
require './chrome-extension.js'
|
||||
|
||||
# Finally load app's main.js and transfer control to C++.
|
||||
module._load path.join(packagePath, packageJson.main), module, true
|
||||
|
|
|
@ -199,6 +199,10 @@ bool AtomRendererClient::IsNodeBindingEnabled(blink::WebFrame* frame) {
|
|||
// Node integration is enabled in main frame unless explictly disabled.
|
||||
else if (frame == main_frame_)
|
||||
return true;
|
||||
// Enable node integration in chrome extensions.
|
||||
else if (frame != NULL &&
|
||||
GURL(frame->document().url()).SchemeIs("chrome-extension"))
|
||||
return true;
|
||||
else if (node_integration_ == MANUAL_ENABLE_IFRAME &&
|
||||
frame != NULL &&
|
||||
frame->uniqueName().utf8().find(kSecurityEnableNodeIntegration)
|
||||
|
|
10
atom/renderer/lib/chrome-api.coffee
Normal file
10
atom/renderer/lib/chrome-api.coffee
Normal file
|
@ -0,0 +1,10 @@
|
|||
url = require 'url'
|
||||
|
||||
chrome = window.chrome = window.chrome || {}
|
||||
chrome.extension =
|
||||
getURL: (path) ->
|
||||
url.format
|
||||
protocol: location.protocol
|
||||
slashes: true
|
||||
hostname: location.hostname
|
||||
pathname: path
|
|
@ -46,6 +46,9 @@ else
|
|||
if location.protocol is 'chrome-devtools:'
|
||||
# Override some inspector APIs.
|
||||
require path.join(__dirname, 'inspector')
|
||||
else if location.protocol is 'chrome-extension:'
|
||||
# Add implementations of chrome API.
|
||||
require path.join(__dirname, 'chrome-api')
|
||||
else
|
||||
# Override default web functions.
|
||||
require path.join(__dirname, 'override')
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* [Application distribution](tutorial/application-distribution.md)
|
||||
* [Use native node modules](tutorial/use-native-node-modules.md)
|
||||
* [Debugging browser process](tutorial/debugging-browser-process.md)
|
||||
* [DevTools extension](tutorial/devtools-extension.md)
|
||||
|
||||
## API references
|
||||
|
||||
|
|
|
@ -138,6 +138,14 @@ Emitted when window loses focus.
|
|||
|
||||
Emitted when window gains focus.
|
||||
|
||||
### Event: 'devtools-opened'
|
||||
|
||||
Emitted when devtools is opened.
|
||||
|
||||
### Event: 'devtools-closed'
|
||||
|
||||
Emitted when devtools is closed.
|
||||
|
||||
### Class Method: BrowserWindow.getAllWindows()
|
||||
|
||||
Returns an array of all opened browser windows.
|
||||
|
@ -158,6 +166,21 @@ Find a window according to the `webContents` it owns
|
|||
|
||||
Find a window according to its ID.
|
||||
|
||||
### Class Method: BrowserWindow.addDevToolsExtension(path)
|
||||
|
||||
* `path` String
|
||||
|
||||
Adds devtools extension located at `path`, and returns extension's name.
|
||||
|
||||
The extension will be remembered so you only need to call this API once, this
|
||||
API is not for programming use.
|
||||
|
||||
### Class Method: BrowserWindow.removeDevToolsExtension(name)
|
||||
|
||||
* `name` String
|
||||
|
||||
Remove the devtools extension whose name is `name`.
|
||||
|
||||
### BrowserWindow.webContents
|
||||
|
||||
The `WebContents` object this window owns, all web page related events and
|
||||
|
@ -341,7 +364,7 @@ Returns the title of the native window.
|
|||
window.
|
||||
|
||||
### BrowserWindow.flashFrame(flag)
|
||||
|
||||
|
||||
* `flag` Boolean
|
||||
|
||||
Starts or stops flashing the window to attract user's attention.
|
||||
|
|
56
docs/tutorial/devtools-extension.md
Normal file
56
docs/tutorial/devtools-extension.md
Normal file
|
@ -0,0 +1,56 @@
|
|||
# DevTools extension
|
||||
|
||||
To make debugging more easy, atom-shell has added basic support for
|
||||
[Chrome DevTools Extension][devtools-extension].
|
||||
|
||||
For most devtools extensions, you can simply download their source codes and use
|
||||
the `BrowserWindow.addDevToolsExtension` API to load them, the loaded extensions
|
||||
will be remembered so you don't need to call the API every time when creating
|
||||
a window.
|
||||
|
||||
For example to use the React DevTools Extension, first you need to download its
|
||||
source code:
|
||||
|
||||
```bash
|
||||
$ cd /some-directory
|
||||
$ git clone --recursive https://github.com/facebook/react-devtools.git
|
||||
```
|
||||
|
||||
Then you can load it in atom-shell by opening the devtools in arbitray window,
|
||||
and run this code in the console of devtools:
|
||||
|
||||
```javascript
|
||||
require('remote').require('browser-window').addDevToolsExtension('/some-directory/react-devtools')
|
||||
```
|
||||
|
||||
To unload the extension, you can call `BrowserWindow.removeDevToolsExtension`
|
||||
API with its name and it will disappear the next time you open the devtools:
|
||||
|
||||
```javascript
|
||||
require('remote').require('browser-window').removeDevToolsExtension('React Developer Tools')
|
||||
```
|
||||
|
||||
## Format of devtools extension
|
||||
|
||||
Ideally all devtools extension written for Chrome browser can be loaded by
|
||||
atom-shell, but they have to be in a plain directory, for those packaged `crx`
|
||||
extensions, there is no way in atom-shell to load them unless you find a way to
|
||||
extract them into a directory.
|
||||
|
||||
## Background pages
|
||||
|
||||
Currently atom-shell doesn't support the background pages of chrome extensions,
|
||||
so for some devtools extensions that rely on this feature, they may not work
|
||||
well in atom-shell
|
||||
|
||||
## `chrome.*` APIs
|
||||
|
||||
Some chrome extensions use `chrome.*` APIs for some features, there is some
|
||||
effort to implement those APIs in atom-shell to make them work, but we have
|
||||
only implemented few for now.
|
||||
|
||||
So if the devtools extension is using APIs other than `chrome.devtools.*`, it is
|
||||
very likely to fail, but you can report those extensions in the issues tracker
|
||||
so we can add support for those APIs.
|
||||
|
||||
[devtools-extension]: https://developer.chrome.com/extensions/devtools
|
|
@ -5,7 +5,7 @@ import sys
|
|||
|
||||
NODE_VERSION = 'v0.11.13'
|
||||
BASE_URL = 'https://gh-contractor-zcbenz.s3.amazonaws.com/libchromiumcontent'
|
||||
LIBCHROMIUMCONTENT_COMMIT = '432720d4613e3aac939f127fe55b9d44fea349e5'
|
||||
LIBCHROMIUMCONTENT_COMMIT = 'afb4570ceee2ad10f3caf5a81335a2ee11ec68a5'
|
||||
|
||||
ARCH = {
|
||||
'cygwin': '32bit',
|
||||
|
|
2
vendor/brightray
vendored
2
vendor/brightray
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 602403baae04c28ab04ea6f8b4f0b1aa697a3630
|
||||
Subproject commit de769889118c6e68ecb0c729be5ab6caef333bf6
|
Loading…
Add table
Add a link
Reference in a new issue