commit
b7d80e792d
119 changed files with 1967 additions and 755 deletions
31
README-ko.md
31
README-ko.md
|
@ -1,4 +1,4 @@
|
|||
[![Electron Logo](http://electron.atom.io/images/electron-logo.svg)](http://electron.atom.io/)
|
||||
[![Electron Logo](http://electron.atom.io/images/electron-logo.svg)](http://electron.atom.io/)
|
||||
|
||||
[![Build Status](https://travis-ci.org/atom/electron.svg?branch=master)](https://travis-ci.org/atom/electron)
|
||||
[![devDependency Status](https://david-dm.org/atom/electron/dev-status.svg)](https://david-dm.org/atom/electron#info=devDependencies)
|
||||
|
@ -8,23 +8,22 @@
|
|||
|
||||
:zap: *이전까지 Atom Shell로 알려져 있었습니다* :zap:
|
||||
|
||||
Electron 프레임워크는 JavaScript, HTML 그리고 CSS를 사용하여 Cross-Platform 데스크톱 어플리케이션을 개발할 수 있도록 해주는 프레임워크입니다. 이 프레임워크는 [io.js](http://iojs.org) 와
|
||||
[Chromium](http://www.chromium.org)을 기반으로 만들어 졌으며 [Atom Editor](https://github.com/atom/atom)에 사용되고 있습니다.
|
||||
|
||||
Electron은 JavaScript, HTML 그리고 CSS를 이용하여 Cross-Platform 데스크톱 어플리케이션을 개발할 수 있도록 해주는 프레임워크입니다. 이 프레임워크는 [io.js](http://iojs.org) 와
|
||||
[Chromium](http://www.chromium.org) 을 기반으로 만들어 졌으며 [Atom Editor](https://github.com/atom/atom) 에 사용되고 있습니다.
|
||||
|
||||
Electron에 대한 중요한 알림을 받으려면 Twitter에서 [@ElectronJS](https://twitter.com/electronjs)를 Follow하세요.
|
||||
Electron에 대한 중요한 알림을 받고 싶다면 Twitter에서 [@ElectronJS](https://twitter.com/electronjs)를 팔로우 하세요.
|
||||
|
||||
## 다운로드
|
||||
|
||||
Linux, Windows, Mac용으로 미리 빌드된 Electron 바이너리와 디버그 심볼이 준비되어 있습니다. [releases](https://github.com/atom/electron/releases) 페이지에서 받아 볼 수 있습니다.
|
||||
|
||||
또한 [`npm`](https://docs.npmjs.com/)을 이용하여 미리 빌드된 Electron 바이너리를 받을 수 있습니다:
|
||||
또한 [`npm`](https://docs.npmjs.com/)을 통해 미리 빌드된 Electron 바이너리를 받을 수도 있습니다:
|
||||
|
||||
```sh
|
||||
# $PATH에 `electron`을 등록하고 전역에 설치합니다.
|
||||
# $PATH에 `electron` 커맨드를 등록하고 전역에 설치합니다.
|
||||
npm install electron-prebuilt -g
|
||||
|
||||
# 개발용 dependency로 설치합니다.
|
||||
# 개발 의존성 모듈 형태로 설치합니다.
|
||||
npm install electron-prebuilt --save-dev
|
||||
```
|
||||
|
||||
|
@ -32,13 +31,19 @@ npm install electron-prebuilt --save-dev
|
|||
|
||||
- [China](https://npm.taobao.org/mirrors/electron)
|
||||
|
||||
## 참조문서
|
||||
## 참조 문서
|
||||
|
||||
[docs](https://github.com/atom/electron/tree/master/docs/README-ko.md) 에 프레임워크 사용 가이드와 API 레퍼런스가 있습니다.
|
||||
추가적으로 Electron을 빌드 하는 방법과 프로젝트에 기여하는 방법이 문서에 포함되어 있으니 참고하시기 바랍니다.
|
||||
[Docs](https://github.com/atom/electron/tree/master/docs/README.md)에 개발 가이드와 API 레퍼런스가 있습니다.
|
||||
Electron을 빌드 하는 방법과 프로젝트에 기여하는 방법도 문서에 포함되어 있으니 참고하시기 바랍니다.
|
||||
|
||||
## 참조 문서(번역)
|
||||
|
||||
- [한국어](https://github.com/atom/electron/tree/master/docs-translations/ko)
|
||||
- [일본어](https://github.com/atom/electron/tree/master/docs-translations/jp)
|
||||
- [스페인어](https://github.com/atom/electron/tree/master/docs-translations/es)
|
||||
|
||||
## 커뮤니티
|
||||
|
||||
[Atom 포럼내의 `electron` 카테고리](http://discuss.atom.io/category/electron) 와 Freenode `#atom-shell` 채팅채널이 있습니다.
|
||||
[Atom 포럼내의 `electron` 카테고리](http://discuss.atom.io/category/electron)와 Freenode `#atom-shell` 채팅 채널에서 활발하게 토론이 이어지고 있습니다.
|
||||
|
||||
[awesome-electron](https://github.com/sindresorhus/awesome-electron) 에 커뮤니티가 운영중인 유용한 예제 앱과 툴, 리소스가 있으니 한번 탐색해 보시기 바랍니다.
|
||||
[awesome-electron](https://github.com/sindresorhus/awesome-electron) 프로젝트엔 커뮤니티가 운영중인 유용한 예제 어플리케이션과 도구, 리소스가 있으니 한번 참고해 보시기 바랍니다.
|
||||
|
|
|
@ -43,6 +43,12 @@ Guides and the API reference are located in the
|
|||
[docs](https://github.com/atom/electron/tree/master/docs) directory. It also
|
||||
contains documents describing how to build and contribute to Electron.
|
||||
|
||||
## Documentation Translations
|
||||
|
||||
- [Korean](https://github.com/atom/electron/tree/master/docs-translations/ko)
|
||||
- [Japanese](https://github.com/atom/electron/tree/master/docs-translations/jp)
|
||||
- [Spanish](https://github.com/atom/electron/tree/master/docs-translations/es)
|
||||
|
||||
## Community
|
||||
|
||||
There is an [`electron` category on the Atom forums](http://discuss.atom.io/category/electron)
|
||||
|
|
|
@ -5,15 +5,18 @@
|
|||
#include "atom/app/node_main.h"
|
||||
|
||||
#include "atom/browser/javascript_environment.h"
|
||||
#include "atom/browser/node_debugger.h"
|
||||
#include "atom/common/node_includes.h"
|
||||
#include "base/command_line.h"
|
||||
#include "gin/array_buffer.h"
|
||||
#include "gin/public/isolate_holder.h"
|
||||
#include "gin/v8_initializer.h"
|
||||
|
||||
#include "atom/common/node_includes.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
int NodeMain(int argc, char *argv[]) {
|
||||
base::CommandLine::Init(argc, argv);
|
||||
|
||||
argv = uv_setup_args(argc, argv);
|
||||
int exec_argc;
|
||||
const char** exec_argv;
|
||||
|
@ -31,17 +34,13 @@ int NodeMain(int argc, char *argv[]) {
|
|||
gin_env.isolate(), uv_default_loop(), gin_env.context(), argc, argv,
|
||||
exec_argc, exec_argv);
|
||||
|
||||
// Start debugger.
|
||||
node::node_isolate = gin_env.isolate();
|
||||
if (node::use_debug_agent)
|
||||
node::StartDebug(env, node::debug_wait_connect);
|
||||
// Start our custom debugger implementation.
|
||||
NodeDebugger node_debugger(gin_env.isolate());
|
||||
if (node_debugger.IsRunning())
|
||||
env->AssignToContext(v8::Debug::GetDebugContext());
|
||||
|
||||
node::LoadEnvironment(env);
|
||||
|
||||
// Enable debugger.
|
||||
if (node::use_debug_agent)
|
||||
node::EnableDebug(env);
|
||||
|
||||
bool more;
|
||||
do {
|
||||
more = uv_run(env->event_loop(), UV_RUN_ONCE);
|
||||
|
|
|
@ -277,7 +277,10 @@ bool WebContents::IsPopupOrPanel(const content::WebContents* source) const {
|
|||
void WebContents::HandleKeyboardEvent(
|
||||
content::WebContents* source,
|
||||
const content::NativeWebKeyboardEvent& event) {
|
||||
if (type_ == BROWSER_WINDOW) {
|
||||
if (event.windowsKeyCode == ui::VKEY_ESCAPE && is_html_fullscreen()) {
|
||||
// Escape exits tabbed fullscreen mode.
|
||||
ExitFullscreenModeForTab(source);
|
||||
} else if (type_ == BROWSER_WINDOW) {
|
||||
owner_window()->HandleKeyboardEvent(source, event);
|
||||
} else if (type_ == WEB_VIEW && guest_delegate_) {
|
||||
// Send the unhandled keyboard events back to the embedder.
|
||||
|
|
|
@ -45,6 +45,8 @@ class EventEmitter : public Wrappable {
|
|||
content::WebContents* sender,
|
||||
IPC::Message* message,
|
||||
const Args&... args) {
|
||||
v8::Locker locker(isolate());
|
||||
v8::HandleScope handle_scope(isolate());
|
||||
v8::Local<v8::Object> event = CreateJSEvent(isolate(), sender, message);
|
||||
return EmitWithEvent(name, event, args...);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "atom/browser/atom_browser_context.h"
|
||||
#include "atom/browser/atom_browser_main_parts.h"
|
||||
#include "atom/common/google_api_key.h"
|
||||
#include "content/public/browser/geolocation_provider.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
|
@ -24,6 +25,7 @@ const char* kGeolocationProviderUrl =
|
|||
} // namespace
|
||||
|
||||
AtomAccessTokenStore::AtomAccessTokenStore() {
|
||||
content::GeolocationProvider::GetInstance()->UserDidOptIntoLocationServices();
|
||||
}
|
||||
|
||||
AtomAccessTokenStore::~AtomAccessTokenStore() {
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "atom/browser/atom_browser_context.h"
|
||||
#include "atom/browser/browser.h"
|
||||
#include "atom/browser/javascript_environment.h"
|
||||
#include "atom/browser/node_debugger.h"
|
||||
#include "atom/common/api/atom_bindings.h"
|
||||
#include "atom/common/node_bindings.h"
|
||||
#include "base/command_line.h"
|
||||
|
@ -69,9 +70,16 @@ void AtomBrowserMainParts::PostEarlyInitialization() {
|
|||
|
||||
node_bindings_->Initialize();
|
||||
|
||||
// Support the "--debug" switch.
|
||||
node_debugger_.reset(new NodeDebugger(js_env_->isolate()));
|
||||
|
||||
// Create the global environment.
|
||||
global_env = node_bindings_->CreateEnvironment(js_env_->context());
|
||||
|
||||
// Make sure node can get correct environment when debugging.
|
||||
if (node_debugger_->IsRunning())
|
||||
global_env->AssignToContext(v8::Debug::GetDebugContext());
|
||||
|
||||
// Add atom-shell extended APIs.
|
||||
atom_bindings_->BindTo(js_env_->isolate(), global_env->process_object());
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ class AtomBindings;
|
|||
class Browser;
|
||||
class JavascriptEnvironment;
|
||||
class NodeBindings;
|
||||
class NodeDebugger;
|
||||
|
||||
class AtomBrowserMainParts : public brightray::BrowserMainParts {
|
||||
public:
|
||||
|
@ -57,6 +58,7 @@ class AtomBrowserMainParts : public brightray::BrowserMainParts {
|
|||
scoped_ptr<JavascriptEnvironment> js_env_;
|
||||
scoped_ptr<NodeBindings> node_bindings_;
|
||||
scoped_ptr<AtomBindings> atom_bindings_;
|
||||
scoped_ptr<NodeDebugger> node_debugger_;
|
||||
|
||||
base::Timer gc_timer_;
|
||||
|
||||
|
|
|
@ -45,7 +45,8 @@ void Browser::Shutdown() {
|
|||
FOR_EACH_OBSERVER(BrowserObserver, observers_, OnQuit());
|
||||
|
||||
is_quiting_ = true;
|
||||
base::MessageLoop::current()->Quit();
|
||||
base::MessageLoop::current()->PostTask(
|
||||
FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
|
||||
}
|
||||
|
||||
std::string Browser::GetVersion() const {
|
||||
|
|
|
@ -48,6 +48,8 @@ class CommonWebContentsDelegate
|
|||
|
||||
NativeWindow* owner_window() const { return owner_window_.get(); }
|
||||
|
||||
bool is_html_fullscreen() const { return html_fullscreen_; }
|
||||
|
||||
protected:
|
||||
// content::WebContentsDelegate:
|
||||
content::WebContents* OpenURLFromTab(
|
||||
|
|
|
@ -30,6 +30,10 @@ guestInstances = {}
|
|||
embedderElementsMap = {}
|
||||
reverseEmbedderElementsMap = {}
|
||||
|
||||
# Moves the last element of array to the first one.
|
||||
moveLastToFirst = (list) ->
|
||||
list.unshift list.pop()
|
||||
|
||||
# Generate guestInstanceId.
|
||||
getNextInstanceId = (webContents) ->
|
||||
++nextInstanceId
|
||||
|
@ -46,7 +50,13 @@ createGuest = (embedder, params) ->
|
|||
destroyEvents = ['destroyed', 'crashed', 'did-navigate-to-different-page']
|
||||
destroy = ->
|
||||
destroyGuest embedder, id if guestInstances[id]?
|
||||
embedder.once event, destroy for event in destroyEvents
|
||||
for event in destroyEvents
|
||||
embedder.once event, destroy
|
||||
# Users might also listen to the crashed event, so We must ensure the guest
|
||||
# is destroyed before users' listener gets called. It is done by moving our
|
||||
# listener to the first one in queue.
|
||||
listeners = embedder._events[event]
|
||||
moveLastToFirst listeners if Array.isArray listeners
|
||||
guest.once 'destroyed', ->
|
||||
embedder.removeListener event, destroy for event in destroyEvents
|
||||
|
||||
|
|
|
@ -92,5 +92,8 @@ app.setAppPath packagePath
|
|||
# Load the chrome extension support.
|
||||
require './chrome-extension'
|
||||
|
||||
# Set main startup script of the app.
|
||||
mainStartupScript = packageJson.main or 'index.js'
|
||||
|
||||
# Finally load app's main.js and transfer control to C++.
|
||||
Module._load path.join(packagePath, packageJson.main), Module, true
|
||||
Module._load path.join(packagePath, mainStartupScript), Module, true
|
||||
|
|
|
@ -1,82 +1,65 @@
|
|||
EventEmitter = require('events').EventEmitter
|
||||
IDWeakMap = process.atomBinding('id_weak_map').IDWeakMap
|
||||
v8Util = process.atomBinding 'v8_util'
|
||||
|
||||
# Class to reference all objects.
|
||||
class ObjectsStore
|
||||
@stores = {}
|
||||
|
||||
constructor: ->
|
||||
@nextId = 0
|
||||
@objects = []
|
||||
|
||||
getNextId: ->
|
||||
++@nextId
|
||||
|
||||
add: (obj) ->
|
||||
id = @getNextId()
|
||||
@objects[id] = obj
|
||||
id
|
||||
|
||||
has: (id) ->
|
||||
@objects[id]?
|
||||
|
||||
remove: (id) ->
|
||||
throw new Error("Invalid key #{id} for ObjectsStore") unless @has id
|
||||
delete @objects[id]
|
||||
|
||||
get: (id) ->
|
||||
throw new Error("Invalid key #{id} for ObjectsStore") unless @has id
|
||||
@objects[id]
|
||||
|
||||
@forRenderView: (key) ->
|
||||
@stores[key] = new ObjectsStore unless @stores[key]?
|
||||
@stores[key]
|
||||
|
||||
@releaseForRenderView: (key) ->
|
||||
delete @stores[key]
|
||||
|
||||
class ObjectsRegistry extends EventEmitter
|
||||
constructor: ->
|
||||
@setMaxListeners Number.MAX_VALUE
|
||||
@nextId = 0
|
||||
|
||||
# Objects in weak map will be not referenced (so we won't leak memory), and
|
||||
# every object created in browser will have a unique id in weak map.
|
||||
@objectsWeakMap = new IDWeakMap
|
||||
@objectsWeakMap.add = (obj) ->
|
||||
id = IDWeakMap::add.call this, obj
|
||||
v8Util.setHiddenValue obj, 'atomId', id
|
||||
id
|
||||
# Stores all objects by ref-counting.
|
||||
# (id) => {object, count}
|
||||
@storage = {}
|
||||
|
||||
# Stores the IDs of objects referenced by WebContents.
|
||||
# (webContentsId) => {(id) => (count)}
|
||||
@owners = {}
|
||||
|
||||
# Register a new object, the object would be kept referenced until you release
|
||||
# it explicitly.
|
||||
add: (key, obj) ->
|
||||
# Some native objects may already been added to objectsWeakMap, be care not
|
||||
# to add it twice.
|
||||
@objectsWeakMap.add obj unless v8Util.getHiddenValue obj, 'atomId'
|
||||
id = v8Util.getHiddenValue obj, 'atomId'
|
||||
add: (webContentsId, obj) ->
|
||||
id = @saveToStorage obj
|
||||
# Remember the owner.
|
||||
@owners[webContentsId] ?= {}
|
||||
@owners[webContentsId][id] ?= 0
|
||||
@owners[webContentsId][id]++
|
||||
# Returns object's id
|
||||
id
|
||||
|
||||
# Store and reference the object, then return the storeId which points to
|
||||
# where the object is stored. The caller can later dereference the object
|
||||
# with the storeId.
|
||||
# We use a difference key because the same object can be referenced for
|
||||
# multiple times by the same renderer view.
|
||||
store = ObjectsStore.forRenderView key
|
||||
storeId = store.add obj
|
||||
|
||||
[id, storeId]
|
||||
|
||||
# Get an object according to its id.
|
||||
# Get an object according to its ID.
|
||||
get: (id) ->
|
||||
@objectsWeakMap.get id
|
||||
@storage[id]?.object
|
||||
|
||||
# Remove an object according to its storeId.
|
||||
remove: (key, storeId) ->
|
||||
ObjectsStore.forRenderView(key).remove storeId
|
||||
# Dereference an object according to its ID.
|
||||
remove: (webContentsId, id) ->
|
||||
@dereference id, 1
|
||||
# Also reduce the count in owner.
|
||||
pointer = @owners[webContentsId]
|
||||
--pointer[id]
|
||||
delete pointer[id] if pointer[id] is 0
|
||||
|
||||
# Clear all references to objects from renderer view.
|
||||
clear: (key) ->
|
||||
@emit "clear-#{key}"
|
||||
ObjectsStore.releaseForRenderView key
|
||||
# Clear all references to objects refrenced by the WebContents.
|
||||
clear: (webContentsId) ->
|
||||
@emit "clear-#{webContentsId}"
|
||||
return unless @owners[webContentsId]?
|
||||
@dereference id, count for id, count of @owners[webContentsId]
|
||||
delete @owners[webContentsId]
|
||||
|
||||
# Private: Saves the object into storage and assigns an ID for it.
|
||||
saveToStorage: (object) ->
|
||||
id = v8Util.getHiddenValue object, 'atomId'
|
||||
unless id
|
||||
id = ++@nextId
|
||||
@storage[id] = {count: 0, object}
|
||||
v8Util.setHiddenValue object, 'atomId', id
|
||||
++@storage[id].count
|
||||
id
|
||||
|
||||
# Private: Dereference the object from store.
|
||||
dereference: (id, count) ->
|
||||
pointer = @storage[id]
|
||||
pointer.count -= count
|
||||
if pointer.count is 0
|
||||
v8Util.deleteHiddenValue pointer.object, 'atomId'
|
||||
delete @storage[id]
|
||||
|
||||
module.exports = new ObjectsRegistry
|
||||
|
|
|
@ -4,7 +4,7 @@ objectsRegistry = require './objects-registry.js'
|
|||
v8Util = process.atomBinding 'v8_util'
|
||||
|
||||
# Convert a real value into meta data.
|
||||
valueToMeta = (sender, value) ->
|
||||
valueToMeta = (sender, value, optimizeSimpleObject=false) ->
|
||||
meta = type: typeof value
|
||||
|
||||
meta.type = 'buffer' if Buffer.isBuffer value
|
||||
|
@ -12,6 +12,10 @@ valueToMeta = (sender, value) ->
|
|||
meta.type = 'array' if Array.isArray value
|
||||
meta.type = 'promise' if value? and value.constructor.name is 'Promise'
|
||||
|
||||
# Treat simple objects as value.
|
||||
if optimizeSimpleObject and meta.type is 'object' and v8Util.getHiddenValue value, 'simple'
|
||||
meta.type = 'value'
|
||||
|
||||
# Treat the arguments object as array.
|
||||
meta.type = 'array' if meta.type is 'object' and value.callee? and value.length?
|
||||
|
||||
|
@ -24,7 +28,7 @@ valueToMeta = (sender, value) ->
|
|||
# Reference the original value if it's an object, because when it's
|
||||
# passed to renderer we would assume the renderer keeps a reference of
|
||||
# it.
|
||||
[meta.id, meta.storeId] = objectsRegistry.add sender.getId(), value
|
||||
meta.id = objectsRegistry.add sender.getId(), value
|
||||
|
||||
meta.members = []
|
||||
meta.members.push {name: prop, type: typeof field} for prop, field of value
|
||||
|
@ -80,11 +84,11 @@ unwrapArgs = (sender, args) ->
|
|||
callFunction = (event, func, caller, args) ->
|
||||
if v8Util.getHiddenValue(func, 'asynchronous') and typeof args[args.length - 1] isnt 'function'
|
||||
args.push (ret) ->
|
||||
event.returnValue = valueToMeta event.sender, ret
|
||||
event.returnValue = valueToMeta event.sender, ret, true
|
||||
func.apply caller, args
|
||||
else
|
||||
ret = func.apply caller, args
|
||||
event.returnValue = valueToMeta event.sender, ret
|
||||
event.returnValue = valueToMeta event.sender, ret, true
|
||||
|
||||
# Send by BrowserWindow when its render view is deleted.
|
||||
process.on 'ATOM_BROWSER_RELEASE_RENDER_VIEW', (id) ->
|
||||
|
@ -170,8 +174,8 @@ ipc.on 'ATOM_BROWSER_MEMBER_GET', (event, id, name) ->
|
|||
catch e
|
||||
event.returnValue = errorToMeta e
|
||||
|
||||
ipc.on 'ATOM_BROWSER_DEREFERENCE', (event, storeId) ->
|
||||
objectsRegistry.remove event.sender.getId(), storeId
|
||||
ipc.on 'ATOM_BROWSER_DEREFERENCE', (event, id) ->
|
||||
objectsRegistry.remove event.sender.getId(), id
|
||||
|
||||
ipc.on 'ATOM_BROWSER_GUEST_WEB_CONTENTS', (event, guestInstanceId) ->
|
||||
try
|
||||
|
|
|
@ -82,9 +82,6 @@ class NativeWindowMac : public NativeWindow {
|
|||
// Called to handle a mouse event.
|
||||
void HandleMouseEvent(NSEvent* event);
|
||||
|
||||
// Clip web view to rounded corner.
|
||||
void ClipWebView();
|
||||
|
||||
protected:
|
||||
// NativeWindow:
|
||||
void HandleKeyboardEvent(
|
||||
|
|
|
@ -22,9 +22,6 @@
|
|||
|
||||
namespace {
|
||||
|
||||
// The radius of rounded corner.
|
||||
const CGFloat kAtomWindowCornerRadius = 4.0;
|
||||
|
||||
// Prevents window from resizing during the scope.
|
||||
class ScopedDisableResize {
|
||||
public:
|
||||
|
@ -41,10 +38,6 @@ bool ScopedDisableResize::disable_resize_ = false;
|
|||
|
||||
} // namespace
|
||||
|
||||
@interface NSView (PrivateMethods)
|
||||
- (CGFloat)roundedCornerRadius;
|
||||
@end
|
||||
|
||||
// This view always takes the size of its superview. It is intended to be used
|
||||
// as a NSWindow's contentView. It is needed because NSWindow's implementation
|
||||
// explicitly resizes the contentView at inopportune times.
|
||||
|
@ -153,9 +146,6 @@ bool ScopedDisableResize::disable_resize_ = false;
|
|||
}
|
||||
|
||||
- (void)windowDidResize:(NSNotification*)notification {
|
||||
if (!shell_->has_frame())
|
||||
shell_->ClipWebView();
|
||||
|
||||
shell_->NotifyWindowResize();
|
||||
}
|
||||
|
||||
|
@ -613,8 +603,8 @@ void NativeWindowMac::Center() {
|
|||
}
|
||||
|
||||
void NativeWindowMac::SetTitle(const std::string& title) {
|
||||
// We don't want the title to show in transparent window.
|
||||
if (transparent())
|
||||
// We don't want the title to show in transparent or frameless window.
|
||||
if (transparent() || !has_frame())
|
||||
return;
|
||||
|
||||
[window_ setTitle:base::SysUTF8ToNSString(title)];
|
||||
|
@ -804,35 +794,27 @@ void NativeWindowMac::HandleKeyboardEvent(
|
|||
}
|
||||
|
||||
void NativeWindowMac::InstallView() {
|
||||
// Make sure the bottom corner is rounded: http://crbug.com/396264.
|
||||
[[window_ contentView] setWantsLayer:YES];
|
||||
|
||||
NSView* view = inspectable_web_contents()->GetView()->GetNativeView();
|
||||
if (has_frame()) {
|
||||
// Add layer with white background for the contents view.
|
||||
base::scoped_nsobject<CALayer> layer([[CALayer alloc] init]);
|
||||
[layer setBackgroundColor:CGColorGetConstantColor(kCGColorWhite)];
|
||||
[view setLayer:layer];
|
||||
[view setFrame:[[window_ contentView] bounds]];
|
||||
[[window_ contentView] addSubview:view];
|
||||
} else {
|
||||
if (base::mac::IsOSYosemiteOrLater()) {
|
||||
// In OSX 10.10, adding subviews to the root view for the NSView hierarchy
|
||||
// produces warnings. To eliminate the warnings, we resize the contentView
|
||||
// to fill the window, and add subviews to that.
|
||||
// http://crbug.com/380412
|
||||
content_view_.reset([[FullSizeContentView alloc] init]);
|
||||
[content_view_
|
||||
setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
|
||||
[content_view_ setFrame:[[[window_ contentView] superview] bounds]];
|
||||
[window_ setContentView:content_view_];
|
||||
// In OSX 10.10, adding subviews to the root view for the NSView hierarchy
|
||||
// produces warnings. To eliminate the warnings, we resize the contentView
|
||||
// to fill the window, and add subviews to that.
|
||||
// http://crbug.com/380412
|
||||
content_view_.reset([[FullSizeContentView alloc] init]);
|
||||
[content_view_
|
||||
setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
|
||||
[content_view_ setFrame:[[[window_ contentView] superview] bounds]];
|
||||
[window_ setContentView:content_view_];
|
||||
|
||||
[view setFrame:[content_view_ bounds]];
|
||||
[content_view_ addSubview:view];
|
||||
} else {
|
||||
NSView* frameView = [[window_ contentView] superview];
|
||||
[view setFrame:[frameView bounds]];
|
||||
[frameView addSubview:view];
|
||||
}
|
||||
[view setFrame:[content_view_ bounds]];
|
||||
[content_view_ addSubview:view];
|
||||
|
||||
ClipWebView();
|
||||
InstallDraggableRegionView();
|
||||
|
||||
[[window_ standardWindowButton:NSWindowZoomButton] setHidden:YES];
|
||||
|
@ -851,14 +833,6 @@ void NativeWindowMac::UninstallView() {
|
|||
[view removeFromSuperview];
|
||||
}
|
||||
|
||||
void NativeWindowMac::ClipWebView() {
|
||||
if (!web_contents())
|
||||
return;
|
||||
NSView* webView = web_contents()->GetNativeView();
|
||||
webView.layer.masksToBounds = YES;
|
||||
webView.layer.cornerRadius = kAtomWindowCornerRadius;
|
||||
}
|
||||
|
||||
void NativeWindowMac::InstallDraggableRegionView() {
|
||||
NSView* webView = web_contents()->GetNativeView();
|
||||
base::scoped_nsobject<NSView> controlRegion(
|
||||
|
|
209
atom/browser/node_debugger.cc
Normal file
209
atom/browser/node_debugger.cc
Normal file
|
@ -0,0 +1,209 @@
|
|||
// Copyright (c) 2014 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/node_debugger.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/command_line.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "net/test/embedded_test_server/tcp_listen_socket.h"
|
||||
|
||||
#include "atom/common/node_includes.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace {
|
||||
|
||||
// NodeDebugger is stored in Isolate's data, slots 0, 1, 3 have already been
|
||||
// taken by gin, blink and node, using 2 is a safe option for now.
|
||||
const int kIsolateSlot = 2;
|
||||
|
||||
const char* kContentLength = "Content-Length";
|
||||
|
||||
} // namespace
|
||||
|
||||
NodeDebugger::NodeDebugger(v8::Isolate* isolate)
|
||||
: isolate_(isolate),
|
||||
thread_("NodeDebugger"),
|
||||
content_length_(-1),
|
||||
weak_factory_(this) {
|
||||
bool use_debug_agent = false;
|
||||
int port = 5858;
|
||||
bool wait_for_connection = false;
|
||||
|
||||
std::string port_str;
|
||||
base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
|
||||
if (cmd->HasSwitch("debug")) {
|
||||
use_debug_agent = true;
|
||||
port_str = cmd->GetSwitchValueASCII("debug");
|
||||
}
|
||||
if (cmd->HasSwitch("debug-brk")) {
|
||||
use_debug_agent = true;
|
||||
wait_for_connection = true;
|
||||
port_str = cmd->GetSwitchValueASCII("debug-brk");
|
||||
}
|
||||
|
||||
if (use_debug_agent) {
|
||||
if (!port_str.empty())
|
||||
base::StringToInt(port_str, &port);
|
||||
|
||||
isolate_->SetData(kIsolateSlot, this);
|
||||
v8::Debug::SetMessageHandler(DebugMessageHandler);
|
||||
|
||||
if (wait_for_connection)
|
||||
v8::Debug::DebugBreak(isolate_);
|
||||
|
||||
uv_async_init(uv_default_loop(), &weak_up_ui_handle_, ProcessMessageInUI);
|
||||
|
||||
// Start a new IO thread.
|
||||
base::Thread::Options options;
|
||||
options.message_loop_type = base::MessageLoop::TYPE_IO;
|
||||
if (!thread_.StartWithOptions(options)) {
|
||||
LOG(ERROR) << "Unable to start debugger thread";
|
||||
return;
|
||||
}
|
||||
|
||||
// Start the server in new IO thread.
|
||||
thread_.message_loop()->PostTask(
|
||||
FROM_HERE,
|
||||
base::Bind(&NodeDebugger::StartServer, weak_factory_.GetWeakPtr(),
|
||||
port));
|
||||
}
|
||||
}
|
||||
|
||||
NodeDebugger::~NodeDebugger() {
|
||||
thread_.Stop();
|
||||
}
|
||||
|
||||
bool NodeDebugger::IsRunning() const {
|
||||
return thread_.IsRunning();
|
||||
}
|
||||
|
||||
void NodeDebugger::StartServer(int port) {
|
||||
server_ = net::test_server::TCPListenSocket::CreateAndListen(
|
||||
"127.0.0.1", port, this);
|
||||
if (!server_) {
|
||||
LOG(ERROR) << "Cannot start debugger server";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void NodeDebugger::CloseSession() {
|
||||
accepted_socket_.reset();
|
||||
}
|
||||
|
||||
void NodeDebugger::OnMessage(const std::string& message) {
|
||||
if (message.find("\"type\":\"request\",\"command\":\"disconnect\"}") !=
|
||||
std::string::npos)
|
||||
CloseSession();
|
||||
|
||||
base::string16 message16 = base::UTF8ToUTF16(message);
|
||||
v8::Debug::SendCommand(
|
||||
isolate_,
|
||||
reinterpret_cast<const uint16_t*>(message16.data()), message16.size());
|
||||
|
||||
uv_async_send(&weak_up_ui_handle_);
|
||||
}
|
||||
|
||||
void NodeDebugger::SendMessage(const std::string& message) {
|
||||
if (accepted_socket_) {
|
||||
std::string header = base::StringPrintf(
|
||||
"%s: %d\r\n\r\n", kContentLength, static_cast<int>(message.size()));
|
||||
accepted_socket_->Send(header);
|
||||
accepted_socket_->Send(message);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeDebugger::SendConnectMessage() {
|
||||
accepted_socket_->Send(base::StringPrintf(
|
||||
"Type: connect\r\n"
|
||||
"V8-Version: %s\r\n"
|
||||
"Protocol-Version: 1\r\n"
|
||||
"Embedding-Host: %s\r\n"
|
||||
"%s: 0\r\n",
|
||||
v8::V8::GetVersion(), ATOM_PRODUCT_NAME, kContentLength), true);
|
||||
}
|
||||
|
||||
// static
|
||||
void NodeDebugger::ProcessMessageInUI(uv_async_t* handle) {
|
||||
v8::Debug::ProcessDebugMessages();
|
||||
}
|
||||
|
||||
// static
|
||||
void NodeDebugger::DebugMessageHandler(const v8::Debug::Message& message) {
|
||||
NodeDebugger* self = static_cast<NodeDebugger*>(
|
||||
message.GetIsolate()->GetData(kIsolateSlot));
|
||||
|
||||
if (self) {
|
||||
std::string message8(*v8::String::Utf8Value(message.GetJSON()));
|
||||
self->thread_.message_loop()->PostTask(
|
||||
FROM_HERE,
|
||||
base::Bind(&NodeDebugger::SendMessage, self->weak_factory_.GetWeakPtr(),
|
||||
message8));
|
||||
}
|
||||
}
|
||||
|
||||
void NodeDebugger::DidAccept(
|
||||
net::test_server::StreamListenSocket* server,
|
||||
scoped_ptr<net::test_server::StreamListenSocket> socket) {
|
||||
// Only accept one session.
|
||||
if (accepted_socket_) {
|
||||
socket->Send(std::string("Remote debugging session already active"), true);
|
||||
return;
|
||||
}
|
||||
|
||||
accepted_socket_ = socket.Pass();
|
||||
SendConnectMessage();
|
||||
}
|
||||
|
||||
void NodeDebugger::DidRead(net::test_server::StreamListenSocket* socket,
|
||||
const char* data,
|
||||
int len) {
|
||||
buffer_.append(data, len);
|
||||
|
||||
do {
|
||||
if (buffer_.size() == 0)
|
||||
return;
|
||||
|
||||
// Read the "Content-Length" header.
|
||||
if (content_length_ < 0) {
|
||||
size_t pos = buffer_.find("\r\n\r\n");
|
||||
if (pos == std::string::npos)
|
||||
return;
|
||||
|
||||
// We can be sure that the header is "Content-Length: xxx\r\n".
|
||||
std::string content_length = buffer_.substr(16, pos - 16);
|
||||
if (!base::StringToInt(content_length, &content_length_)) {
|
||||
DidClose(accepted_socket_.get());
|
||||
return;
|
||||
}
|
||||
|
||||
// Strip header from buffer.
|
||||
buffer_ = buffer_.substr(pos + 4);
|
||||
}
|
||||
|
||||
// Read the message.
|
||||
if (buffer_.size() >= static_cast<size_t>(content_length_)) {
|
||||
std::string message = buffer_.substr(0, content_length_);
|
||||
buffer_ = buffer_.substr(content_length_);
|
||||
|
||||
OnMessage(message);
|
||||
|
||||
// Get ready for next message.
|
||||
content_length_ = -1;
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
|
||||
void NodeDebugger::DidClose(net::test_server::StreamListenSocket* socket) {
|
||||
// If we lost the connection, then simulate a disconnect msg:
|
||||
OnMessage("{\"seq\":1,\"type\":\"request\",\"command\":\"disconnect\"}");
|
||||
}
|
||||
|
||||
} // namespace atom
|
65
atom/browser/node_debugger.h
Normal file
65
atom/browser/node_debugger.h
Normal file
|
@ -0,0 +1,65 @@
|
|||
// Copyright (c) 2014 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NODE_DEBUGGER_H_
|
||||
#define ATOM_BROWSER_NODE_DEBUGGER_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/threading/thread.h"
|
||||
#include "net/test/embedded_test_server/stream_listen_socket.h"
|
||||
#include "v8/include/v8-debug.h"
|
||||
#include "vendor/node/deps/uv/include/uv.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
// Add support for node's "--debug" switch.
|
||||
class NodeDebugger : public net::test_server::StreamListenSocket::Delegate {
|
||||
public:
|
||||
explicit NodeDebugger(v8::Isolate* isolate);
|
||||
virtual ~NodeDebugger();
|
||||
|
||||
bool IsRunning() const;
|
||||
|
||||
private:
|
||||
void StartServer(int port);
|
||||
void CloseSession();
|
||||
void OnMessage(const std::string& message);
|
||||
void SendMessage(const std::string& message);
|
||||
void SendConnectMessage();
|
||||
|
||||
static void ProcessMessageInUI(uv_async_t* handle);
|
||||
|
||||
static void DebugMessageHandler(const v8::Debug::Message& message);
|
||||
|
||||
// net::test_server::StreamListenSocket::Delegate:
|
||||
void DidAccept(
|
||||
net::test_server::StreamListenSocket* server,
|
||||
scoped_ptr<net::test_server::StreamListenSocket> socket) override;
|
||||
void DidRead(net::test_server::StreamListenSocket* socket,
|
||||
const char* data,
|
||||
int len) override;
|
||||
void DidClose(net::test_server::StreamListenSocket* socket) override;
|
||||
|
||||
v8::Isolate* isolate_;
|
||||
|
||||
uv_async_t weak_up_ui_handle_;
|
||||
|
||||
base::Thread thread_;
|
||||
scoped_ptr<net::test_server::StreamListenSocket> server_;
|
||||
scoped_ptr<net::test_server::StreamListenSocket> accepted_socket_;
|
||||
|
||||
std::string buffer_;
|
||||
int content_length_;
|
||||
|
||||
base::WeakPtrFactory<NodeDebugger> weak_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(NodeDebugger);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_NODE_DEBUGGER_H_
|
|
@ -1,6 +1,21 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- Windows 10 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||
<!-- Windows 8.1 -->
|
||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
|
||||
<!-- Windows 8 -->
|
||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
|
||||
<!-- Windows 7 -->
|
||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
|
||||
<!-- Windows Vista -->
|
||||
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
|
||||
</application>
|
||||
</compatibility>
|
||||
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity type="Win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"></assemblyIdentity>
|
||||
|
|
|
@ -19,6 +19,9 @@ namespace {
|
|||
// Makes sure that .jpg also shows .JPG.
|
||||
gboolean FileFilterCaseInsensitive(const GtkFileFilterInfo* file_info,
|
||||
std::string* file_extension) {
|
||||
// Makes .* file extension matches all file types.
|
||||
if (*file_extension == ".*")
|
||||
return true;
|
||||
return EndsWith(file_info->filename, *file_extension, false);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,12 @@ void SetAllowedFileTypes(NSSavePanel* dialog, const Filters& filters) {
|
|||
for (size_t i = 0; i < filters.size(); ++i) {
|
||||
const Filter& filter = filters[i];
|
||||
for (size_t j = 0; j < filter.second.size(); ++j) {
|
||||
// If we meet a '*' file extension, we allow all the file types and no
|
||||
// need to set the specified file types.
|
||||
if (filter.second[j] == "*") {
|
||||
[dialog setAllowsOtherFileTypes:YES];
|
||||
return;
|
||||
}
|
||||
base::ScopedCFTypeRef<CFStringRef> ext_cf(
|
||||
base::SysUTF8ToCFStringRef(filter.second[j]));
|
||||
[file_type_set addObject:base::mac::CFToNSCast(ext_cf.get())];
|
||||
|
|
|
@ -78,9 +78,9 @@ int ShowMessageBoxUTF16(HWND parent,
|
|||
const base::string16& message,
|
||||
const base::string16& detail,
|
||||
const gfx::ImageSkia& icon) {
|
||||
TASKDIALOG_FLAGS flags = TDF_SIZE_TO_CONTENT; // show all content.
|
||||
if (cancel_id != 0)
|
||||
flags |= TDF_ALLOW_DIALOG_CANCELLATION; // allow dialog to be cancelled.
|
||||
TASKDIALOG_FLAGS flags =
|
||||
TDF_SIZE_TO_CONTENT | // Show all content.
|
||||
TDF_ALLOW_DIALOG_CANCELLATION; // Allow canceling the dialog.
|
||||
|
||||
TASKDIALOGCONFIG config = { 0 };
|
||||
config.cbSize = sizeof(config);
|
||||
|
|
|
@ -207,8 +207,14 @@ const CGFloat kVerticalTitleMargin = 2;
|
|||
}
|
||||
inMouseEventSequence_ = NO;
|
||||
|
||||
// Show menu when single clicked on the icon.
|
||||
if (event.clickCount == 1 && menuController_)
|
||||
// Show menu when there is a context menu.
|
||||
// NB(hokein): Make tray's behavior more like official one's.
|
||||
// When the tray icon gets clicked quickly multiple times, the
|
||||
// event.clickCount doesn't always return 1. Instead, it returns a value that
|
||||
// counts the clicked times.
|
||||
// So we don't check the clickCount here, just pop up the menu for each click
|
||||
// event.
|
||||
if (menuController_)
|
||||
[statusItem_ popUpStatusItemMenu:[menuController_ menu]];
|
||||
|
||||
// Don't emit click events when menu is showing.
|
||||
|
|
|
@ -1,80 +0,0 @@
|
|||
// Copyright (c) 2013 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/common/api/atom_api_id_weak_map.h"
|
||||
|
||||
#include "native_mate/constructor.h"
|
||||
#include "native_mate/object_template_builder.h"
|
||||
|
||||
#include "atom/common/node_includes.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace api {
|
||||
|
||||
IDWeakMap::IDWeakMap() {
|
||||
}
|
||||
|
||||
IDWeakMap::~IDWeakMap() {
|
||||
}
|
||||
|
||||
int32_t IDWeakMap::Add(v8::Isolate* isolate, v8::Local<v8::Object> object) {
|
||||
return map_.Add(isolate, object);
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> IDWeakMap::Get(v8::Isolate* isolate, int32_t key) {
|
||||
v8::MaybeLocal<v8::Object> result = map_.Get(isolate, key);
|
||||
if (result.IsEmpty()) {
|
||||
isolate->ThrowException(v8::Exception::Error(
|
||||
mate::StringToV8(isolate, "Invalid key")));
|
||||
return v8::Undefined(isolate);
|
||||
} else {
|
||||
return result.ToLocalChecked();
|
||||
}
|
||||
}
|
||||
|
||||
bool IDWeakMap::Has(int32_t key) const {
|
||||
return map_.Has(key);
|
||||
}
|
||||
|
||||
std::vector<int32_t> IDWeakMap::Keys() const {
|
||||
return map_.Keys();
|
||||
}
|
||||
|
||||
void IDWeakMap::Remove(int32_t key) {
|
||||
map_.Remove(key);
|
||||
}
|
||||
|
||||
// static
|
||||
void IDWeakMap::BuildPrototype(v8::Isolate* isolate,
|
||||
v8::Local<v8::ObjectTemplate> prototype) {
|
||||
mate::ObjectTemplateBuilder(isolate, prototype)
|
||||
.SetMethod("add", &IDWeakMap::Add)
|
||||
.SetMethod("get", &IDWeakMap::Get)
|
||||
.SetMethod("has", &IDWeakMap::Has)
|
||||
.SetMethod("keys", &IDWeakMap::Keys)
|
||||
.SetMethod("remove", &IDWeakMap::Remove);
|
||||
}
|
||||
|
||||
} // namespace api
|
||||
|
||||
} // namespace atom
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
|
||||
v8::Local<v8::Context> context, void* priv) {
|
||||
using atom::api::IDWeakMap;
|
||||
v8::Isolate* isolate = context->GetIsolate();
|
||||
v8::Local<v8::Function> constructor = mate::CreateConstructor<IDWeakMap>(
|
||||
isolate,
|
||||
"IDWeakMap",
|
||||
base::Bind(&mate::NewOperatorFactory<IDWeakMap>));
|
||||
exports->Set(mate::StringToSymbol(isolate, "IDWeakMap"), constructor);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_common_id_weak_map, Initialize)
|
|
@ -1,44 +0,0 @@
|
|||
// Copyright (c) 2013 GitHub, Inc.
|
||||
// Copyright (c) 2012 Intel Corp. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_COMMON_API_ATOM_API_ID_WEAK_MAP_H_
|
||||
#define ATOM_COMMON_API_ATOM_API_ID_WEAK_MAP_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "atom/common/id_weak_map.h"
|
||||
#include "native_mate/wrappable.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace api {
|
||||
|
||||
// Like ES6's WeakMap, but the key is Integer and the value is Weak Pointer.
|
||||
class IDWeakMap : public mate::Wrappable {
|
||||
public:
|
||||
IDWeakMap();
|
||||
|
||||
static void BuildPrototype(v8::Isolate* isolate,
|
||||
v8::Local<v8::ObjectTemplate> prototype);
|
||||
|
||||
private:
|
||||
virtual ~IDWeakMap();
|
||||
|
||||
int32_t Add(v8::Isolate* isolate, v8::Local<v8::Object> object);
|
||||
v8::Local<v8::Value> Get(v8::Isolate* isolate, int32_t key);
|
||||
bool Has(int32_t key) const;
|
||||
std::vector<int32_t> Keys() const;
|
||||
void Remove(int32_t key);
|
||||
|
||||
atom::IDWeakMap map_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(IDWeakMap);
|
||||
};
|
||||
|
||||
} // namespace api
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_COMMON_API_ATOM_API_ID_WEAK_MAP_H_
|
|
@ -28,6 +28,11 @@ void SetHiddenValue(v8::Local<v8::Object> object,
|
|||
object->SetHiddenValue(key, value);
|
||||
}
|
||||
|
||||
void DeleteHiddenValue(v8::Local<v8::Object> object,
|
||||
v8::Local<v8::String> key) {
|
||||
object->DeleteHiddenValue(key);
|
||||
}
|
||||
|
||||
int32_t GetObjectHash(v8::Local<v8::Object> object) {
|
||||
return object->GetIdentityHash();
|
||||
}
|
||||
|
@ -48,6 +53,7 @@ void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
|
|||
dict.SetMethod("createObjectWithName", &CreateObjectWithName);
|
||||
dict.SetMethod("getHiddenValue", &GetHiddenValue);
|
||||
dict.SetMethod("setHiddenValue", &SetHiddenValue);
|
||||
dict.SetMethod("deleteHiddenValue", &DeleteHiddenValue);
|
||||
dict.SetMethod("getObjectHash", &GetObjectHash);
|
||||
dict.SetMethod("setDestructor", &SetDestructor);
|
||||
dict.SetMethod("takeHeapSnapshot", &TakeHeapSnapshot);
|
||||
|
|
|
@ -5,32 +5,52 @@
|
|||
|
||||
#include "atom/common/api/object_life_monitor.h"
|
||||
|
||||
#include "native_mate/compat.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/message_loop/message_loop.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
// static
|
||||
void ObjectLifeMonitor::BindTo(v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> target,
|
||||
v8::Local<v8::Value> destructor) {
|
||||
target->SetHiddenValue(MATE_STRING_NEW(isolate, "destructor"), destructor);
|
||||
|
||||
ObjectLifeMonitor* olm = new ObjectLifeMonitor();
|
||||
olm->handle_.reset(isolate, target);
|
||||
olm->handle_.SetWeak(olm, WeakCallback);
|
||||
v8::Local<v8::Function> destructor) {
|
||||
new ObjectLifeMonitor(isolate, target, destructor);
|
||||
}
|
||||
|
||||
ObjectLifeMonitor::ObjectLifeMonitor() {
|
||||
ObjectLifeMonitor::ObjectLifeMonitor(v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> target,
|
||||
v8::Local<v8::Function> destructor)
|
||||
: isolate_(isolate),
|
||||
context_(isolate, isolate->GetCurrentContext()),
|
||||
target_(isolate, target),
|
||||
destructor_(isolate, destructor),
|
||||
weak_ptr_factory_(this) {
|
||||
target_.SetWeak(this, OnObjectGC, v8::WeakCallbackType::kParameter);
|
||||
}
|
||||
|
||||
// static
|
||||
void ObjectLifeMonitor::WeakCallback(
|
||||
const v8::WeakCallbackData<v8::Object, ObjectLifeMonitor>& data) {
|
||||
// destructor.call(object, object);
|
||||
v8::Local<v8::Object> obj = data.GetValue();
|
||||
v8::Local<v8::Function>::Cast(obj->GetHiddenValue(
|
||||
MATE_STRING_NEW(data.GetIsolate(), "destructor")))->Call(obj, 0, NULL);
|
||||
delete data.GetParameter();
|
||||
void ObjectLifeMonitor::OnObjectGC(
|
||||
const v8::WeakCallbackInfo<ObjectLifeMonitor>& data) {
|
||||
// Usually FirstWeakCallback should do nothing other than reset |object_|
|
||||
// and then set a second weak callback to run later. We can sidestep that,
|
||||
// because posting a task to the current message loop is all but free - but
|
||||
// DO NOT add any more work to this method. The only acceptable place to add
|
||||
// code is RunCallback.
|
||||
ObjectLifeMonitor* self = data.GetParameter();
|
||||
self->target_.Reset();
|
||||
base::MessageLoop::current()->PostTask(
|
||||
FROM_HERE, base::Bind(&ObjectLifeMonitor::RunCallback,
|
||||
self->weak_ptr_factory_.GetWeakPtr()));
|
||||
}
|
||||
|
||||
void ObjectLifeMonitor::RunCallback() {
|
||||
v8::HandleScope handle_scope(isolate_);
|
||||
v8::Local<v8::Context> context = v8::Local<v8::Context>::New(
|
||||
isolate_, context_);
|
||||
v8::Context::Scope context_scope(context);
|
||||
v8::Local<v8::Function>::New(isolate_, destructor_)->Call(
|
||||
context->Global(), 0, nullptr);
|
||||
delete this;
|
||||
}
|
||||
|
||||
} // namespace atom
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
#define ATOM_COMMON_API_OBJECT_LIFE_MONITOR_H_
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "native_mate/scoped_persistent.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "v8/include/v8.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
|
@ -14,15 +15,23 @@ class ObjectLifeMonitor {
|
|||
public:
|
||||
static void BindTo(v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> target,
|
||||
v8::Local<v8::Value> destructor);
|
||||
v8::Local<v8::Function> destructor);
|
||||
|
||||
private:
|
||||
ObjectLifeMonitor();
|
||||
ObjectLifeMonitor(v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> target,
|
||||
v8::Local<v8::Function> destructor);
|
||||
|
||||
static void WeakCallback(
|
||||
const v8::WeakCallbackData<v8::Object, ObjectLifeMonitor>& data);
|
||||
static void OnObjectGC(const v8::WeakCallbackInfo<ObjectLifeMonitor>& data);
|
||||
|
||||
mate::ScopedPersistent<v8::Object> handle_;
|
||||
void RunCallback();
|
||||
|
||||
v8::Isolate* isolate_;
|
||||
v8::Global<v8::Context> context_;
|
||||
v8::Global<v8::Object> target_;
|
||||
v8::Global<v8::Function> destructor_;
|
||||
|
||||
base::WeakPtrFactory<ObjectLifeMonitor> weak_ptr_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ObjectLifeMonitor);
|
||||
};
|
||||
|
|
|
@ -10,6 +10,22 @@
|
|||
|
||||
namespace atom {
|
||||
|
||||
namespace {
|
||||
|
||||
struct ObjectKey {
|
||||
ObjectKey(int id, IDWeakMap* map) : id(id), map(map) {}
|
||||
int id;
|
||||
IDWeakMap* map;
|
||||
};
|
||||
|
||||
void OnObjectGC(const v8::WeakCallbackInfo<ObjectKey>& data) {
|
||||
ObjectKey* key = data.GetParameter();
|
||||
key->map->Remove(key->id);
|
||||
delete key;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
IDWeakMap::IDWeakMap() : next_id_(0) {
|
||||
}
|
||||
|
||||
|
@ -18,11 +34,9 @@ IDWeakMap::~IDWeakMap() {
|
|||
|
||||
int32_t IDWeakMap::Add(v8::Isolate* isolate, v8::Local<v8::Object> object) {
|
||||
int32_t id = GetNextID();
|
||||
object->SetHiddenValue(mate::StringToSymbol(isolate, "IDWeakMapKey"),
|
||||
mate::Converter<int32_t>::ToV8(isolate, id));
|
||||
|
||||
auto global = make_linked_ptr(new v8::Global<v8::Object>(isolate, object));
|
||||
global->SetWeak(this, &WeakCallback);
|
||||
ObjectKey* key = new ObjectKey(id, this);
|
||||
global->SetWeak(key, OnObjectGC, v8::WeakCallbackType::kParameter);
|
||||
map_[id] = global;
|
||||
return id;
|
||||
}
|
||||
|
@ -71,12 +85,4 @@ int32_t IDWeakMap::GetNextID() {
|
|||
return ++next_id_;
|
||||
}
|
||||
|
||||
// static
|
||||
void IDWeakMap::WeakCallback(
|
||||
const v8::WeakCallbackData<v8::Object, IDWeakMap>& data) {
|
||||
int32_t id = data.GetValue()->GetHiddenValue(
|
||||
mate::StringToV8(data.GetIsolate(), "IDWeakMapKey"))->Int32Value();
|
||||
data.GetParameter()->Remove(id);
|
||||
}
|
||||
|
||||
} // namespace atom
|
||||
|
|
|
@ -44,9 +44,6 @@ class IDWeakMap {
|
|||
// Returns next available ID.
|
||||
int32_t GetNextID();
|
||||
|
||||
static void WeakCallback(
|
||||
const v8::WeakCallbackData<v8::Object, IDWeakMap>& data);
|
||||
|
||||
// ID of next stored object.
|
||||
int32_t next_id_;
|
||||
|
||||
|
|
|
@ -14,7 +14,8 @@ namespace mate {
|
|||
|
||||
v8::Local<v8::Value> Converter<gfx::Point>::ToV8(v8::Isolate* isolate,
|
||||
const gfx::Point& val) {
|
||||
mate::Dictionary dict(isolate, v8::Object::New(isolate));
|
||||
mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
|
||||
dict.SetHidden("simple", true);
|
||||
dict.Set("x", val.x());
|
||||
dict.Set("y", val.y());
|
||||
return dict.GetHandle();
|
||||
|
@ -35,7 +36,8 @@ bool Converter<gfx::Point>::FromV8(v8::Isolate* isolate,
|
|||
|
||||
v8::Local<v8::Value> Converter<gfx::Size>::ToV8(v8::Isolate* isolate,
|
||||
const gfx::Size& val) {
|
||||
mate::Dictionary dict(isolate, v8::Object::New(isolate));
|
||||
mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
|
||||
dict.SetHidden("simple", true);
|
||||
dict.Set("width", val.width());
|
||||
dict.Set("height", val.height());
|
||||
return dict.GetHandle();
|
||||
|
@ -56,7 +58,8 @@ bool Converter<gfx::Size>::FromV8(v8::Isolate* isolate,
|
|||
|
||||
v8::Local<v8::Value> Converter<gfx::Rect>::ToV8(v8::Isolate* isolate,
|
||||
const gfx::Rect& val) {
|
||||
mate::Dictionary dict(isolate, v8::Object::New(isolate));
|
||||
mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
|
||||
dict.SetHidden("simple", true);
|
||||
dict.Set("x", val.x());
|
||||
dict.Set("y", val.y());
|
||||
dict.Set("width", val.width());
|
||||
|
@ -95,7 +98,8 @@ struct Converter<gfx::Display::TouchSupport> {
|
|||
|
||||
v8::Local<v8::Value> Converter<gfx::Display>::ToV8(v8::Isolate* isolate,
|
||||
const gfx::Display& val) {
|
||||
mate::Dictionary dict(isolate, v8::Object::New(isolate));
|
||||
mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
|
||||
dict.SetHidden("simple", true);
|
||||
dict.Set("id", val.id());
|
||||
dict.Set("bounds", val.bounds());
|
||||
dict.Set("workArea", val.work_area());
|
||||
|
|
|
@ -11,13 +11,14 @@
|
|||
#include "base/logging.h"
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "base/values.h"
|
||||
#include "native_mate/dictionary.h"
|
||||
#include "vendor/node/src/node_buffer.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace {
|
||||
|
||||
const int kMaxRecursionDepth = 20;
|
||||
const int kMaxRecursionDepth = 100;
|
||||
|
||||
} // namespace
|
||||
|
||||
|
@ -179,7 +180,8 @@ v8::Local<v8::Value> V8ValueConverter::ToV8Array(
|
|||
|
||||
v8::Local<v8::Value> V8ValueConverter::ToV8Object(
|
||||
v8::Isolate* isolate, const base::DictionaryValue* val) const {
|
||||
v8::Local<v8::Object> result(v8::Object::New(isolate));
|
||||
mate::Dictionary result = mate::Dictionary::CreateEmpty(isolate);
|
||||
result.SetHidden("simple", true);
|
||||
|
||||
for (base::DictionaryValue::Iterator iter(*val);
|
||||
!iter.IsAtEnd(); iter.Advance()) {
|
||||
|
@ -188,17 +190,14 @@ v8::Local<v8::Value> V8ValueConverter::ToV8Object(
|
|||
CHECK(!child_v8.IsEmpty());
|
||||
|
||||
v8::TryCatch try_catch;
|
||||
result->Set(
|
||||
v8::String::NewFromUtf8(isolate, key.c_str(), v8::String::kNormalString,
|
||||
key.length()),
|
||||
child_v8);
|
||||
result.Set(key, child_v8);
|
||||
if (try_catch.HasCaught()) {
|
||||
LOG(ERROR) << "Setter for property " << key.c_str() << " threw an "
|
||||
<< "exception.";
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return result.GetHandle();
|
||||
}
|
||||
|
||||
base::Value* V8ValueConverter::FromV8ValueImpl(
|
||||
|
|
|
@ -47,7 +47,6 @@ REFERENCE_MODULE(atom_browser_window);
|
|||
REFERENCE_MODULE(atom_common_asar);
|
||||
REFERENCE_MODULE(atom_common_clipboard);
|
||||
REFERENCE_MODULE(atom_common_crash_reporter);
|
||||
REFERENCE_MODULE(atom_common_id_weak_map);
|
||||
REFERENCE_MODULE(atom_common_native_image);
|
||||
REFERENCE_MODULE(atom_common_screen);
|
||||
REFERENCE_MODULE(atom_common_shell);
|
||||
|
@ -137,11 +136,6 @@ void NodeBindings::Initialize() {
|
|||
AtomCommandLine::InitializeFromCommandLine();
|
||||
#endif
|
||||
|
||||
// Parse the debug args.
|
||||
auto args = AtomCommandLine::argv();
|
||||
for (const std::string& arg : args)
|
||||
node::ParseDebugOpt(arg.c_str());
|
||||
|
||||
// Init node.
|
||||
// (we assume node::Init would not modify the parameters under embedded mode).
|
||||
node::Init(nullptr, nullptr, nullptr, nullptr);
|
||||
|
@ -179,15 +173,7 @@ node::Environment* NodeBindings::CreateEnvironment(
|
|||
}
|
||||
|
||||
void NodeBindings::LoadEnvironment(node::Environment* env) {
|
||||
node::node_isolate = env->isolate();
|
||||
if (node::use_debug_agent)
|
||||
node::StartDebug(env, node::debug_wait_connect);
|
||||
|
||||
node::LoadEnvironment(env);
|
||||
|
||||
if (node::use_debug_agent)
|
||||
node::EnableDebug(env);
|
||||
|
||||
mate::EmitEvent(env->isolate(), env->process_object(), "loaded");
|
||||
}
|
||||
|
||||
|
|
|
@ -50,6 +50,10 @@ double WebFrame::GetZoomFactor() const {
|
|||
return blink::WebView::zoomLevelToZoomFactor(GetZoomLevel());
|
||||
}
|
||||
|
||||
void WebFrame::SetZoomLevelLimits(double min_level, double max_level) {
|
||||
web_frame_->view()->setDefaultPageScaleLimits(min_level, max_level);
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> WebFrame::RegisterEmbedderCustomElement(
|
||||
const base::string16& name, v8::Local<v8::Object> options) {
|
||||
blink::WebExceptionCode c = 0;
|
||||
|
@ -102,6 +106,7 @@ mate::ObjectTemplateBuilder WebFrame::GetObjectTemplateBuilder(
|
|||
.SetMethod("getZoomLevel", &WebFrame::GetZoomLevel)
|
||||
.SetMethod("setZoomFactor", &WebFrame::SetZoomFactor)
|
||||
.SetMethod("getZoomFactor", &WebFrame::GetZoomFactor)
|
||||
.SetMethod("setZoomLevelLimits", &WebFrame::SetZoomLevelLimits)
|
||||
.SetMethod("registerEmbedderCustomElement",
|
||||
&WebFrame::RegisterEmbedderCustomElement)
|
||||
.SetMethod("registerElementResizeCallback",
|
||||
|
|
|
@ -41,6 +41,8 @@ class WebFrame : public mate::Wrappable {
|
|||
double SetZoomFactor(double factor);
|
||||
double GetZoomFactor() const;
|
||||
|
||||
void SetZoomLevelLimits(double min_level, double max_level);
|
||||
|
||||
v8::Local<v8::Value> RegisterEmbedderCustomElement(
|
||||
const base::string16& name, v8::Local<v8::Object> options);
|
||||
void RegisterElementResizeCallback(
|
||||
|
|
|
@ -102,7 +102,7 @@ metaToValue = (meta) ->
|
|||
# Track delegate object's life time, and tell the browser to clean up
|
||||
# when the object is GCed.
|
||||
v8Util.setDestructor ret, ->
|
||||
ipc.send 'ATOM_BROWSER_DEREFERENCE', meta.storeId
|
||||
ipc.send 'ATOM_BROWSER_DEREFERENCE', meta.id
|
||||
|
||||
# Remember object's id.
|
||||
v8Util.setHiddenValue ret, 'atomId', meta.id
|
||||
|
|
|
@ -80,12 +80,15 @@ window.alert = (message, title='') ->
|
|||
buttons = ['OK']
|
||||
message = message.toString()
|
||||
dialog.showMessageBox remote.getCurrentWindow(), {message, title, buttons}
|
||||
# Alert should always return undefined.
|
||||
return
|
||||
|
||||
# And the confirm().
|
||||
window.confirm = (message, title='') ->
|
||||
dialog = remote.require 'dialog'
|
||||
buttons = ['OK', 'Cancel']
|
||||
not dialog.showMessageBox remote.getCurrentWindow(), {message, title, buttons}
|
||||
cancelId = 1
|
||||
not dialog.showMessageBox remote.getCurrentWindow(), {message, title, buttons, cancelId}
|
||||
|
||||
# But we do not support prompt().
|
||||
window.prompt = ->
|
||||
|
|
|
@ -0,0 +1,330 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "net/test/embedded_test_server/stream_listen_socket.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
// winsock2.h must be included first in order to ensure it is included before
|
||||
// windows.h.
|
||||
#include <winsock2.h>
|
||||
#elif defined(OS_POSIX)
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include "net/base/net_errors.h"
|
||||
#endif
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "base/posix/eintr_wrapper.h"
|
||||
#include "base/sys_byteorder.h"
|
||||
#include "base/threading/platform_thread.h"
|
||||
#include "build/build_config.h"
|
||||
#include "net/base/ip_endpoint.h"
|
||||
#include "net/base/net_errors.h"
|
||||
#include "net/base/net_util.h"
|
||||
#include "net/socket/socket_descriptor.h"
|
||||
|
||||
using std::string;
|
||||
|
||||
#if defined(OS_WIN)
|
||||
typedef int socklen_t;
|
||||
#endif // defined(OS_WIN)
|
||||
|
||||
namespace net {
|
||||
|
||||
namespace test_server {
|
||||
|
||||
namespace {
|
||||
|
||||
const int kReadBufSize = 4096;
|
||||
|
||||
} // namespace
|
||||
|
||||
#if defined(OS_WIN)
|
||||
const int StreamListenSocket::kSocketError = SOCKET_ERROR;
|
||||
#elif defined(OS_POSIX)
|
||||
const int StreamListenSocket::kSocketError = -1;
|
||||
#endif
|
||||
|
||||
StreamListenSocket::StreamListenSocket(SocketDescriptor s,
|
||||
StreamListenSocket::Delegate* del)
|
||||
: socket_delegate_(del),
|
||||
socket_(s),
|
||||
reads_paused_(false),
|
||||
has_pending_reads_(false) {
|
||||
#if defined(OS_WIN)
|
||||
socket_event_ = WSACreateEvent();
|
||||
// TODO(ibrar): error handling in case of socket_event_ == WSA_INVALID_EVENT.
|
||||
WatchSocket(NOT_WAITING);
|
||||
#elif defined(OS_POSIX)
|
||||
wait_state_ = NOT_WAITING;
|
||||
#endif
|
||||
}
|
||||
|
||||
StreamListenSocket::~StreamListenSocket() {
|
||||
CloseSocket();
|
||||
#if defined(OS_WIN)
|
||||
if (socket_event_) {
|
||||
WSACloseEvent(socket_event_);
|
||||
socket_event_ = WSA_INVALID_EVENT;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void StreamListenSocket::Send(const char* bytes,
|
||||
int len,
|
||||
bool append_linefeed) {
|
||||
SendInternal(bytes, len);
|
||||
if (append_linefeed)
|
||||
SendInternal("\r\n", 2);
|
||||
}
|
||||
|
||||
void StreamListenSocket::Send(const string& str, bool append_linefeed) {
|
||||
Send(str.data(), static_cast<int>(str.length()), append_linefeed);
|
||||
}
|
||||
|
||||
int StreamListenSocket::GetLocalAddress(IPEndPoint* address) const {
|
||||
SockaddrStorage storage;
|
||||
if (getsockname(socket_, storage.addr, &storage.addr_len)) {
|
||||
#if defined(OS_WIN)
|
||||
int err = WSAGetLastError();
|
||||
#else
|
||||
int err = errno;
|
||||
#endif
|
||||
return MapSystemError(err);
|
||||
}
|
||||
if (!address->FromSockAddr(storage.addr, storage.addr_len))
|
||||
return ERR_ADDRESS_INVALID;
|
||||
return OK;
|
||||
}
|
||||
|
||||
int StreamListenSocket::GetPeerAddress(IPEndPoint* address) const {
|
||||
SockaddrStorage storage;
|
||||
if (getpeername(socket_, storage.addr, &storage.addr_len)) {
|
||||
#if defined(OS_WIN)
|
||||
int err = WSAGetLastError();
|
||||
#else
|
||||
int err = errno;
|
||||
#endif
|
||||
return MapSystemError(err);
|
||||
}
|
||||
|
||||
if (!address->FromSockAddr(storage.addr, storage.addr_len))
|
||||
return ERR_ADDRESS_INVALID;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
SocketDescriptor StreamListenSocket::AcceptSocket() {
|
||||
SocketDescriptor conn = HANDLE_EINTR(accept(socket_, NULL, NULL));
|
||||
if (conn == kInvalidSocket)
|
||||
LOG(ERROR) << "Error accepting connection.";
|
||||
else
|
||||
SetNonBlocking(conn);
|
||||
return conn;
|
||||
}
|
||||
|
||||
void StreamListenSocket::SendInternal(const char* bytes, int len) {
|
||||
char* send_buf = const_cast<char*>(bytes);
|
||||
int len_left = len;
|
||||
while (true) {
|
||||
int sent = HANDLE_EINTR(send(socket_, send_buf, len_left, 0));
|
||||
if (sent == len_left) { // A shortcut to avoid extraneous checks.
|
||||
break;
|
||||
}
|
||||
if (sent == kSocketError) {
|
||||
#if defined(OS_WIN)
|
||||
if (WSAGetLastError() != WSAEWOULDBLOCK) {
|
||||
LOG(ERROR) << "send failed: WSAGetLastError()==" << WSAGetLastError();
|
||||
#elif defined(OS_POSIX)
|
||||
if (errno != EWOULDBLOCK && errno != EAGAIN) {
|
||||
LOG(ERROR) << "send failed: errno==" << errno;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
// Otherwise we would block, and now we have to wait for a retry.
|
||||
// Fall through to PlatformThread::YieldCurrentThread()
|
||||
} else {
|
||||
// sent != len_left according to the shortcut above.
|
||||
// Shift the buffer start and send the remainder after a short while.
|
||||
send_buf += sent;
|
||||
len_left -= sent;
|
||||
}
|
||||
base::PlatformThread::YieldCurrentThread();
|
||||
}
|
||||
}
|
||||
|
||||
void StreamListenSocket::Listen() {
|
||||
int backlog = 10; // TODO(erikkay): maybe don't allow any backlog?
|
||||
if (listen(socket_, backlog) == -1) {
|
||||
// TODO(erikkay): error handling.
|
||||
LOG(ERROR) << "Could not listen on socket.";
|
||||
return;
|
||||
}
|
||||
#if defined(OS_POSIX)
|
||||
WatchSocket(WAITING_ACCEPT);
|
||||
#endif
|
||||
}
|
||||
|
||||
void StreamListenSocket::Read() {
|
||||
char buf[kReadBufSize + 1]; // +1 for null termination.
|
||||
int len;
|
||||
do {
|
||||
len = HANDLE_EINTR(recv(socket_, buf, kReadBufSize, 0));
|
||||
if (len == kSocketError) {
|
||||
#if defined(OS_WIN)
|
||||
int err = WSAGetLastError();
|
||||
if (err == WSAEWOULDBLOCK) {
|
||||
#elif defined(OS_POSIX)
|
||||
if (errno == EWOULDBLOCK || errno == EAGAIN) {
|
||||
#endif
|
||||
break;
|
||||
} else {
|
||||
// TODO(ibrar): some error handling required here.
|
||||
break;
|
||||
}
|
||||
} else if (len == 0) {
|
||||
#if defined(OS_POSIX)
|
||||
// In Windows, Close() is called by OnObjectSignaled. In POSIX, we need
|
||||
// to call it here.
|
||||
Close();
|
||||
#endif
|
||||
} else {
|
||||
// TODO(ibrar): maybe change DidRead to take a length instead.
|
||||
DCHECK_GT(len, 0);
|
||||
DCHECK_LE(len, kReadBufSize);
|
||||
buf[len] = 0; // Already create a buffer with +1 length.
|
||||
socket_delegate_->DidRead(this, buf, len);
|
||||
}
|
||||
} while (len == kReadBufSize);
|
||||
}
|
||||
|
||||
void StreamListenSocket::Close() {
|
||||
#if defined(OS_POSIX)
|
||||
if (wait_state_ == NOT_WAITING)
|
||||
return;
|
||||
wait_state_ = NOT_WAITING;
|
||||
#endif
|
||||
UnwatchSocket();
|
||||
socket_delegate_->DidClose(this);
|
||||
}
|
||||
|
||||
void StreamListenSocket::CloseSocket() {
|
||||
if (socket_ != kInvalidSocket) {
|
||||
UnwatchSocket();
|
||||
#if defined(OS_WIN)
|
||||
closesocket(socket_);
|
||||
#elif defined(OS_POSIX)
|
||||
close(socket_);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void StreamListenSocket::WatchSocket(WaitState state) {
|
||||
#if defined(OS_WIN)
|
||||
WSAEventSelect(socket_, socket_event_, FD_ACCEPT | FD_CLOSE | FD_READ);
|
||||
watcher_.StartWatching(socket_event_, this);
|
||||
#elif defined(OS_POSIX)
|
||||
// Implicitly calls StartWatchingFileDescriptor().
|
||||
base::MessageLoopForIO::current()->WatchFileDescriptor(
|
||||
socket_, true, base::MessageLoopForIO::WATCH_READ, &watcher_, this);
|
||||
wait_state_ = state;
|
||||
#endif
|
||||
}
|
||||
|
||||
void StreamListenSocket::UnwatchSocket() {
|
||||
#if defined(OS_WIN)
|
||||
watcher_.StopWatching();
|
||||
#elif defined(OS_POSIX)
|
||||
watcher_.StopWatchingFileDescriptor();
|
||||
#endif
|
||||
}
|
||||
|
||||
// TODO(ibrar): We can add these functions into OS dependent files.
|
||||
#if defined(OS_WIN)
|
||||
// MessageLoop watcher callback.
|
||||
void StreamListenSocket::OnObjectSignaled(HANDLE object) {
|
||||
WSANETWORKEVENTS ev;
|
||||
if (kSocketError == WSAEnumNetworkEvents(socket_, socket_event_, &ev)) {
|
||||
// TODO
|
||||
return;
|
||||
}
|
||||
|
||||
// If both FD_CLOSE and FD_READ are set we only call Read().
|
||||
// This will cause OnObjectSignaled to be called immediately again
|
||||
// unless this socket is destroyed in Read().
|
||||
if ((ev.lNetworkEvents & (FD_CLOSE | FD_READ)) == FD_CLOSE) {
|
||||
Close();
|
||||
// Close might have deleted this object. We should return immediately.
|
||||
return;
|
||||
}
|
||||
// The object was reset by WSAEnumNetworkEvents. Watch for the next signal.
|
||||
watcher_.StartWatching(object, this);
|
||||
|
||||
if (ev.lNetworkEvents == 0) {
|
||||
// Occasionally the event is set even though there is no new data.
|
||||
// The net seems to think that this is ignorable.
|
||||
return;
|
||||
}
|
||||
if (ev.lNetworkEvents & FD_ACCEPT) {
|
||||
Accept();
|
||||
}
|
||||
if (ev.lNetworkEvents & FD_READ) {
|
||||
if (reads_paused_) {
|
||||
has_pending_reads_ = true;
|
||||
} else {
|
||||
Read();
|
||||
// Read might have deleted this object. We should return immediately.
|
||||
}
|
||||
}
|
||||
}
|
||||
#elif defined(OS_POSIX)
|
||||
void StreamListenSocket::OnFileCanReadWithoutBlocking(int fd) {
|
||||
switch (wait_state_) {
|
||||
case WAITING_ACCEPT:
|
||||
Accept();
|
||||
break;
|
||||
case WAITING_READ:
|
||||
if (reads_paused_) {
|
||||
has_pending_reads_ = true;
|
||||
} else {
|
||||
Read();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Close() is called by Read() in the Linux case.
|
||||
NOTREACHED();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void StreamListenSocket::OnFileCanWriteWithoutBlocking(int fd) {
|
||||
// MessagePumpLibevent callback, we don't listen for write events
|
||||
// so we shouldn't ever reach here.
|
||||
NOTREACHED();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void StreamListenSocket::PauseReads() {
|
||||
DCHECK(!reads_paused_);
|
||||
reads_paused_ = true;
|
||||
}
|
||||
|
||||
void StreamListenSocket::ResumeReads() {
|
||||
DCHECK(reads_paused_);
|
||||
reads_paused_ = false;
|
||||
if (has_pending_reads_) {
|
||||
has_pending_reads_ = false;
|
||||
Read();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace test_server
|
||||
|
||||
} // namespace net
|
|
@ -0,0 +1,151 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Stream-based listen socket implementation that handles reading and writing
|
||||
// to the socket, but does not handle creating the socket nor connecting
|
||||
// sockets, which are handled by subclasses on creation and in Accept,
|
||||
// respectively.
|
||||
|
||||
// StreamListenSocket handles IO asynchronously in the specified MessageLoop.
|
||||
// This class is NOT thread safe. It uses WSAEVENT handles to monitor activity
|
||||
// in a given MessageLoop. This means that callbacks will happen in that loop's
|
||||
// thread always and that all other methods (including constructor and
|
||||
// destructor) should also be called from the same thread.
|
||||
|
||||
#ifndef NET_TEST_EMBEDDED_TEST_SERVER_STREAM_LISTEN_SOCKET_H_
|
||||
#define NET_TEST_EMBEDDED_TEST_SERVER_STREAM_LISTEN_SOCKET_H_
|
||||
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
#include <string>
|
||||
#if defined(OS_WIN)
|
||||
#include "base/win/object_watcher.h"
|
||||
#elif defined(OS_POSIX)
|
||||
#include "base/message_loop/message_loop.h"
|
||||
#endif
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "net/base/net_export.h"
|
||||
#include "net/socket/socket_descriptor.h"
|
||||
|
||||
namespace net {
|
||||
|
||||
class IPEndPoint;
|
||||
|
||||
namespace test_server {
|
||||
|
||||
class StreamListenSocket :
|
||||
#if defined(OS_WIN)
|
||||
public base::win::ObjectWatcher::Delegate {
|
||||
#elif defined(OS_POSIX)
|
||||
public base::MessageLoopForIO::Watcher {
|
||||
#endif
|
||||
|
||||
public:
|
||||
~StreamListenSocket() override;
|
||||
|
||||
// TODO(erikkay): this delegate should really be split into two parts
|
||||
// to split up the listener from the connected socket. Perhaps this class
|
||||
// should be split up similarly.
|
||||
class Delegate {
|
||||
public:
|
||||
// |server| is the original listening Socket, connection is the new
|
||||
// Socket that was created.
|
||||
virtual void DidAccept(StreamListenSocket* server,
|
||||
scoped_ptr<StreamListenSocket> connection) = 0;
|
||||
virtual void DidRead(StreamListenSocket* connection,
|
||||
const char* data,
|
||||
int len) = 0;
|
||||
virtual void DidClose(StreamListenSocket* sock) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~Delegate() {}
|
||||
};
|
||||
|
||||
// Send data to the socket.
|
||||
void Send(const char* bytes, int len, bool append_linefeed = false);
|
||||
void Send(const std::string& str, bool append_linefeed = false);
|
||||
|
||||
// Copies the local address to |address|. Returns a network error code.
|
||||
// This method is virtual to support unit testing.
|
||||
virtual int GetLocalAddress(IPEndPoint* address) const;
|
||||
// Copies the peer address to |address|. Returns a network error code.
|
||||
// This method is virtual to support unit testing.
|
||||
virtual int GetPeerAddress(IPEndPoint* address) const;
|
||||
|
||||
static const int kSocketError;
|
||||
|
||||
protected:
|
||||
enum WaitState { NOT_WAITING = 0, WAITING_ACCEPT = 1, WAITING_READ = 2 };
|
||||
|
||||
StreamListenSocket(SocketDescriptor s, Delegate* del);
|
||||
|
||||
SocketDescriptor AcceptSocket();
|
||||
virtual void Accept() = 0;
|
||||
|
||||
void Listen();
|
||||
void Read();
|
||||
void Close();
|
||||
void CloseSocket();
|
||||
|
||||
// Pass any value in case of Windows, because in Windows
|
||||
// we are not using state.
|
||||
void WatchSocket(WaitState state);
|
||||
void UnwatchSocket();
|
||||
|
||||
Delegate* const socket_delegate_;
|
||||
|
||||
private:
|
||||
friend class TransportClientSocketTest;
|
||||
|
||||
void SendInternal(const char* bytes, int len);
|
||||
|
||||
#if defined(OS_WIN)
|
||||
// ObjectWatcher delegate.
|
||||
void OnObjectSignaled(HANDLE object) override;
|
||||
base::win::ObjectWatcher watcher_;
|
||||
HANDLE socket_event_;
|
||||
#elif defined(OS_POSIX)
|
||||
// Called by MessagePumpLibevent when the socket is ready to do I/O.
|
||||
void OnFileCanReadWithoutBlocking(int fd) override;
|
||||
void OnFileCanWriteWithoutBlocking(int fd) override;
|
||||
WaitState wait_state_;
|
||||
// The socket's libevent wrapper.
|
||||
base::MessageLoopForIO::FileDescriptorWatcher watcher_;
|
||||
#endif
|
||||
|
||||
// NOTE: This is for unit test use only!
|
||||
// Pause/Resume calling Read(). Note that ResumeReads() will also call
|
||||
// Read() if there is anything to read.
|
||||
void PauseReads();
|
||||
void ResumeReads();
|
||||
|
||||
const SocketDescriptor socket_;
|
||||
bool reads_paused_;
|
||||
bool has_pending_reads_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(StreamListenSocket);
|
||||
};
|
||||
|
||||
// Abstract factory that must be subclassed for each subclass of
|
||||
// StreamListenSocket.
|
||||
class StreamListenSocketFactory {
|
||||
public:
|
||||
virtual ~StreamListenSocketFactory() {}
|
||||
|
||||
// Returns a new instance of StreamListenSocket or NULL if an error occurred.
|
||||
virtual scoped_ptr<StreamListenSocket> CreateAndListen(
|
||||
StreamListenSocket::Delegate* delegate) const = 0;
|
||||
};
|
||||
|
||||
} // namespace test_server
|
||||
|
||||
} // namespace net
|
||||
|
||||
#endif // NET_TEST_EMBEDDED_TEST_SERVER_STREAM_LISTEN_SOCKET_H_
|
118
chromium_src/net/test/embedded_test_server/tcp_listen_socket.cc
Normal file
118
chromium_src/net/test/embedded_test_server/tcp_listen_socket.cc
Normal file
|
@ -0,0 +1,118 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "net/test/embedded_test_server/tcp_listen_socket.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
// winsock2.h must be included first in order to ensure it is included before
|
||||
// windows.h.
|
||||
#include <winsock2.h>
|
||||
#elif defined(OS_POSIX)
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include "net/base/net_errors.h"
|
||||
#endif
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/sys_byteorder.h"
|
||||
#include "base/threading/platform_thread.h"
|
||||
#include "build/build_config.h"
|
||||
#include "net/base/net_util.h"
|
||||
#include "net/base/winsock_init.h"
|
||||
#include "net/socket/socket_descriptor.h"
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace net {
|
||||
|
||||
namespace test_server {
|
||||
|
||||
// static
|
||||
scoped_ptr<TCPListenSocket> TCPListenSocket::CreateAndListen(
|
||||
const string& ip,
|
||||
uint16 port,
|
||||
StreamListenSocket::Delegate* del) {
|
||||
SocketDescriptor s = CreateAndBind(ip, port);
|
||||
if (s == kInvalidSocket)
|
||||
return scoped_ptr<TCPListenSocket>();
|
||||
scoped_ptr<TCPListenSocket> sock(new TCPListenSocket(s, del));
|
||||
sock->Listen();
|
||||
return sock.Pass();
|
||||
}
|
||||
|
||||
TCPListenSocket::TCPListenSocket(SocketDescriptor s,
|
||||
StreamListenSocket::Delegate* del)
|
||||
: StreamListenSocket(s, del) {
|
||||
}
|
||||
|
||||
TCPListenSocket::~TCPListenSocket() {
|
||||
}
|
||||
|
||||
SocketDescriptor TCPListenSocket::CreateAndBind(const string& ip, uint16 port) {
|
||||
SocketDescriptor s = CreatePlatformSocket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (s != kInvalidSocket) {
|
||||
#if defined(OS_POSIX)
|
||||
// Allow rapid reuse.
|
||||
static const int kOn = 1;
|
||||
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn));
|
||||
#endif
|
||||
sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = inet_addr(ip.c_str());
|
||||
addr.sin_port = base::HostToNet16(port);
|
||||
if (bind(s, reinterpret_cast<sockaddr*>(&addr), sizeof(addr))) {
|
||||
#if defined(OS_WIN)
|
||||
closesocket(s);
|
||||
#elif defined(OS_POSIX)
|
||||
close(s);
|
||||
#endif
|
||||
LOG(ERROR) << "Could not bind socket to " << ip << ":" << port;
|
||||
s = kInvalidSocket;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
SocketDescriptor TCPListenSocket::CreateAndBindAnyPort(const string& ip,
|
||||
uint16* port) {
|
||||
SocketDescriptor s = CreateAndBind(ip, 0);
|
||||
if (s == kInvalidSocket)
|
||||
return kInvalidSocket;
|
||||
sockaddr_in addr;
|
||||
socklen_t addr_size = sizeof(addr);
|
||||
bool failed = getsockname(s, reinterpret_cast<struct sockaddr*>(&addr),
|
||||
&addr_size) != 0;
|
||||
if (addr_size != sizeof(addr))
|
||||
failed = true;
|
||||
if (failed) {
|
||||
LOG(ERROR) << "Could not determine bound port, getsockname() failed";
|
||||
#if defined(OS_WIN)
|
||||
closesocket(s);
|
||||
#elif defined(OS_POSIX)
|
||||
close(s);
|
||||
#endif
|
||||
return kInvalidSocket;
|
||||
}
|
||||
*port = base::NetToHost16(addr.sin_port);
|
||||
return s;
|
||||
}
|
||||
|
||||
void TCPListenSocket::Accept() {
|
||||
SocketDescriptor conn = AcceptSocket();
|
||||
if (conn == kInvalidSocket)
|
||||
return;
|
||||
scoped_ptr<TCPListenSocket> sock(new TCPListenSocket(conn, socket_delegate_));
|
||||
#if defined(OS_POSIX)
|
||||
sock->WatchSocket(WAITING_READ);
|
||||
#endif
|
||||
socket_delegate_->DidAccept(this, sock.Pass());
|
||||
}
|
||||
|
||||
} // namespace test_server
|
||||
|
||||
} // namespace net
|
|
@ -0,0 +1,55 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef NET_TEST_EMBEDDED_TEST_SERVER_TCP_LISTEN_SOCKET_H_
|
||||
#define NET_TEST_EMBEDDED_TEST_SERVER_TCP_LISTEN_SOCKET_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "net/base/net_export.h"
|
||||
#include "net/socket/socket_descriptor.h"
|
||||
#include "net/test/embedded_test_server/stream_listen_socket.h"
|
||||
|
||||
namespace net {
|
||||
|
||||
namespace test_server {
|
||||
|
||||
// Implements a TCP socket.
|
||||
class TCPListenSocket : public StreamListenSocket {
|
||||
public:
|
||||
~TCPListenSocket() override;
|
||||
|
||||
// Listen on port for the specified IP address. Use 127.0.0.1 to only
|
||||
// accept local connections.
|
||||
static scoped_ptr<TCPListenSocket> CreateAndListen(
|
||||
const std::string& ip,
|
||||
uint16 port,
|
||||
StreamListenSocket::Delegate* del);
|
||||
|
||||
protected:
|
||||
TCPListenSocket(SocketDescriptor s, StreamListenSocket::Delegate* del);
|
||||
|
||||
// Implements StreamListenSocket::Accept.
|
||||
void Accept() override;
|
||||
|
||||
private:
|
||||
friend class EmbeddedTestServer;
|
||||
friend class TCPListenSocketTester;
|
||||
|
||||
// Get raw TCP socket descriptor bound to ip:port.
|
||||
static SocketDescriptor CreateAndBind(const std::string& ip, uint16 port);
|
||||
|
||||
// Get raw TCP socket descriptor bound to ip and return port it is bound to.
|
||||
static SocketDescriptor CreateAndBindAnyPort(const std::string& ip,
|
||||
uint16* port);
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TCPListenSocket);
|
||||
};
|
||||
|
||||
} // namespace test_server
|
||||
|
||||
} // namespace net
|
||||
|
||||
#endif // NET_TEST_EMBEDDED_TEST_SERVER_TCP_LISTEN_SOCKET_H_
|
68
docs-translations/ko/README.md
Normal file
68
docs-translations/ko/README.md
Normal file
|
@ -0,0 +1,68 @@
|
|||
## 개발 가이드
|
||||
|
||||
* [어플리케이션 배포](tutorial/application-distribution.md)
|
||||
* [어플리케이션 패키징](tutorial/application-packaging.md)
|
||||
* [네이티브 node 모듈 사용하기](tutorial/using-native-node-modules.md)
|
||||
* [메인 프로세스 디버깅하기](tutorial/debugging-main-process.md)
|
||||
* [Selenium 과 WebDriver 사용하기](tutorial/using-selenium-and-webdriver.md)
|
||||
* [개발자 콘솔 확장기능](tutorial/devtools-extension.md)
|
||||
* [Pepper 플래시 플러그인 사용하기](tutorial/using-pepper-flash-plugin.md)
|
||||
|
||||
## 튜토리얼
|
||||
|
||||
* [시작하기](tutorial/quick-start.md)
|
||||
* [데스크톱 환경 통합](tutorial/desktop-environment-integration.md)
|
||||
* [온라인/오프라인 이벤트](tutorial/online-offline-events.md)
|
||||
|
||||
## API 레퍼런스
|
||||
|
||||
* [개요](api/synopsis.md)
|
||||
* [프로세스 객체](api/process.md)
|
||||
* [크롬 Command-Line 스위치 지원](api/chrome-command-line-switches.md)
|
||||
|
||||
커스텀 DOM Element:
|
||||
|
||||
* [`File` 객체](api/file-object.md)
|
||||
* [`<webview>` 태그](api/web-view-tag.md)
|
||||
* [`window.open` 함수](api/window-open.md)
|
||||
|
||||
메인 프로세스를 위한 모듈들:
|
||||
|
||||
* [app](api/app.md)
|
||||
* [auto-updater](api/auto-updater.md)
|
||||
* [browser-window](api/browser-window.md)
|
||||
* [content-tracing](api/content-tracing.md)
|
||||
* [dialog](api/dialog.md)
|
||||
* [global-shortcut](api/global-shortcut.md)
|
||||
* [ipc (main process)](api/ipc-main-process.md)
|
||||
* [menu](api/menu.md)
|
||||
* [menu-item](api/menu-item.md)
|
||||
* [power-monitor](api/power-monitor.md)
|
||||
* [power-save-blocker](api/power-save-blocker.md)
|
||||
* [protocol](api/protocol.md)
|
||||
* [tray](api/tray.md)
|
||||
|
||||
랜더러 프로세스를 위한 모듈들 (웹 페이지):
|
||||
|
||||
* [ipc (renderer)](api/ipc-renderer.md)
|
||||
* [remote](api/remote.md)
|
||||
* [web-frame](api/web-frame.md)
|
||||
|
||||
두 프로세스에서 모두 사용 가능한 모듈들:
|
||||
|
||||
* [clipboard](api/clipboard.md)
|
||||
* [crash-reporter](api/crash-reporter.md)
|
||||
* [native-image](api/native-image.md)
|
||||
* [screen](api/screen.md)
|
||||
* [shell](api/shell.md)
|
||||
|
||||
## 개발자용
|
||||
|
||||
* [코딩 스타일](development/coding-style.md)
|
||||
* [소스 코드 디렉터리 구조](development/source-code-directory-structure.md)
|
||||
* [NW.js와 기술적으로 다른점 (이전 node-webkit)](development/atom-shell-vs-node-webkit.md)
|
||||
* [빌드 시스템 개요](development/build-system-overview.md)
|
||||
* [빌드 설명서 (Mac)](development/build-instructions-mac.md)
|
||||
* [빌드 설명서 (Windows)](development/build-instructions-windows.md)
|
||||
* [빌드 설명서 (Linux)](development/build-instructions-linux.md)
|
||||
* [디버거에서 디버그 심볼 서버 설정](development/setting-up-symbol-server.md)
|
|
@ -1,4 +1,4 @@
|
|||
# auto-updater
|
||||
# autoUpdater
|
||||
|
||||
**이 모듈은 현재 OS X에서만 사용할 수 있습니다.**
|
||||
|
||||
|
@ -76,26 +76,30 @@ Squirrel은 "url"로 `Accept: application/zip` 헤더와 함께 업데이트 zip
|
|||
|
||||
`pub_date`은 ISO 8601 표준에 따라 포맷된 날짜입니다.
|
||||
|
||||
## Event: error
|
||||
## Events
|
||||
|
||||
`autoUpdater` 객체는 다음과 같은 이벤트를 발생시킵니다:
|
||||
|
||||
### Event: 'error'
|
||||
|
||||
* `event` Event
|
||||
* `message` String
|
||||
|
||||
업데이트시 에러가 나면 발생하는 이벤트입니다.
|
||||
|
||||
## Event: checking-for-update
|
||||
### Event: 'checking-for-update'
|
||||
|
||||
업데이트를 확인하기 시작할 때 발생하는 이벤트입니다.
|
||||
|
||||
## Event: update-available
|
||||
### Event: 'update-available'
|
||||
|
||||
사용 가능한 업데이트가 있을 때 발생하는 이벤트입니다. 이벤트는 자동으로 다운로드 됩니다.
|
||||
|
||||
## Event: update-not-available
|
||||
### Event: 'update-not-available'
|
||||
|
||||
사용 가능한 업데이트가 없을 때 발생하는 이벤트입니다.
|
||||
|
||||
## Event: update-downloaded
|
||||
### Event: 'update-downloaded'
|
||||
|
||||
* `event` Event
|
||||
* `releaseNotes` String
|
||||
|
@ -106,12 +110,16 @@ Squirrel은 "url"로 `Accept: application/zip` 헤더와 함께 업데이트 zip
|
|||
|
||||
업데이트의 다운로드가 완료되었을 때 발생하는 이벤트입니다. `quitAndUpdate()`를 호출하면 어플리케이션을 종료하고 업데이트를 설치합니다.
|
||||
|
||||
## autoUpdater.setFeedUrl(url)
|
||||
## Methods
|
||||
|
||||
`autoUpdater` 객체에서 사용할 수 있는 메서드입니다:
|
||||
|
||||
### `autoUpdater.setFeedUrl(url)`
|
||||
|
||||
* `url` String
|
||||
|
||||
`url`을 설정하고 자동 업데이터를 초기화합니다. `url`은 한번 설정되면 변경할 수 없습니다.
|
||||
|
||||
## autoUpdater.checkForUpdates()
|
||||
### `autoUpdater.checkForUpdates()`
|
||||
|
||||
서버에 새로운 업데이트가 있는지 요청을 보내 확인합니다. API를 사용하기 전에 `setFeedUrl`를 호출해야 합니다.
|
|
@ -62,9 +62,9 @@ Hostname 맵핑 규칙을 설정합니다. (`,`로 분리)
|
|||
|
||||
`--host-rules` 플래그와 비슷하지만 이 플래그는 host resolver에만 적용됩니다.
|
||||
|
||||
[app]: app-ko.md
|
||||
[append-switch]: app-ko.md#appcommandlineappendswitchswitch-value
|
||||
[ready]: app-ko.md#event-ready
|
||||
[app]: app.md
|
||||
[append-switch]: app.md#appcommandlineappendswitchswitch-value
|
||||
[ready]: app.md#event-ready
|
||||
|
||||
## --ignore-certificate-errors
|
||||
|
|
@ -45,11 +45,11 @@ console.log(clipboard.readText('selection'));
|
|||
|
||||
* `type` String
|
||||
|
||||
클립보드로부터 [NativeImage](native-image-ko.md)로 이미지를 읽어들입니다.
|
||||
클립보드로부터 [NativeImage](native-image.md)로 이미지를 읽어들입니다.
|
||||
|
||||
## clipboard.writeImage(image[, type])
|
||||
|
||||
* `image` [NativeImage](native-image-ko.md)
|
||||
* `image` [NativeImage](native-image.md)
|
||||
* `type` String
|
||||
|
||||
클립보드에 `image`를 씁니다.
|
|
@ -72,8 +72,8 @@ tracing.startRecording('*', tracing.DEFAULT_OPTIONS, function() {
|
|||
|
||||
Child 프로세스는 일반적으로 추적 데이터와 희귀한 플러시 그리고 추적 데이터를 메인 프로세스로 보내는 작업에 대해 캐싱 합니다.
|
||||
이러한 일을 하는 이유는 IPC를 통해 추적 데이터를 보내는 작업은 매우 비싼 연산을 동반하기 때문입니다.
|
||||
우리는 추적에 의한 런타임 오버헤드를 피하는 것을 지향합니다.
|
||||
그래서 트레이싱이 끝나면 모든 child 프로세스에 보류된 추적 데이터를 플러시 할 것인지 물어봅니다.
|
||||
우리는 추적에 의한 런타임 오버헤드를 피하고자 합니다.
|
||||
그래서 추적이 끝나면 모든 child 프로세스에 보류된 추적 데이터를 플러시 할 것인지 물어봅니다.
|
||||
|
||||
모든 child 프로세스가 `stopRecording` 요청을 받으면 `callback`에 추적 데이터를 포함한 파일을 전달됩니다.
|
||||
|
||||
|
@ -107,21 +107,18 @@ Child 프로세스는 일반적으로 추적 데이터와 희귀한 플러시
|
|||
|
||||
현재 모니터링 추적 데이터를 가져옵니다.
|
||||
|
||||
Child processes typically are caching trace data and only rarely flush and send
|
||||
trace data back to the main process. That is because it may be an expensive
|
||||
operation to send the trace data over IPC, and we would like to avoid unneeded
|
||||
runtime overhead of tracing. So, to end tracing, we must asynchronously ask all
|
||||
child processes to flush any pending trace data.
|
||||
자식 프로세스들은 일반적으로 추적 데이터를 캐싱하며 드물게 플러시 하거나 메인 프로세스로 추적 데이터를 보냅니다.
|
||||
왜냐하면 IPC를 통해 추적 데이터를 보내는데에는 많은 자원을 소비하기 때문입니다.
|
||||
그리고 우리는 추적시 발생하는 불필요한 런타임 오버헤드를 피하고자 합니다.
|
||||
그래서 추적이 끝나면 반드시 비동기로 자식 프로세스들의 보류된 추적 데이터를 플러시 할 것인지 물어봅니다.
|
||||
|
||||
Once all child processes have acked to the `captureMonitoringSnapshot` request,
|
||||
the `callback` will be invoked with a file that contains the traced data.
|
||||
모든 자식 프로세스가 `captureMonitoringSnapshot` 요청을 받으면 추적 데이터 파일을 포함하는 `callback`이 호출됩니다.
|
||||
|
||||
## tracing.getTraceBufferUsage(callback)
|
||||
|
||||
* `callback` Function
|
||||
|
||||
Get the maximum across processes of trace buffer percent full state. When the
|
||||
TraceBufferUsage value is determined, the `callback` is called.
|
||||
추적 버퍼 % 전체 상태의 프로세스간 최대치를 가져옵니다. TraceBufferUsage 값이 결정되면 `callback`이 호출됩니다.
|
||||
|
||||
## tracing.setWatchEvent(categoryName, eventName, callback)
|
||||
|
||||
|
@ -129,11 +126,8 @@ TraceBufferUsage value is determined, the `callback` is called.
|
|||
* `eventName` String
|
||||
* `callback` Function
|
||||
|
||||
`callback` will will be called every time the given event occurs on any
|
||||
process.
|
||||
`callback`은 지정된 이벤트가 어떤 작업을 발생시킬 때마다 호출됩니다.
|
||||
|
||||
## tracing.cancelWatchEvent()
|
||||
|
||||
Watch 이벤트를 중단합니다. 만약 추적이 활성화되어 있다면 이 함수는 watch 이벤트 콜백과 race가 일어날 것입니다.
|
||||
|
||||
Cancel the watch event. If tracing is enabled, this may race with the watch event callback.
|
|
@ -35,11 +35,16 @@ console.log(dialog.showOpenDialog({ properties: [ 'openFile', 'openDirectory', '
|
|||
filters: [
|
||||
{ name: 'Images', extensions: ['jpg', 'png', 'gif'] },
|
||||
{ name: 'Movies', extensions: ['mkv', 'avi', 'mp4'] },
|
||||
{ name: 'Custom File Type', extensions: ['as'] }
|
||||
{ name: 'Custom File Type', extensions: ['as'] },
|
||||
{ name: 'All Files', extensions: ['*'] }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
`extensions` 배열은 반드시 와일드카드와 마침표를 제외한 파일 확장자를 포함시켜야 합니다.
|
||||
예를 들어 `'png'`는 가능하지만 `'.png'`와 `'*.png'`는 안됩니다.
|
||||
모든 파일을 보여주려면 `'*'`와 같은 와일드카드를 사용하면 됩니다. (다른 와일드카드는 지원하지 않습니다)
|
||||
|
||||
`callback`이 전달되면 메소드가 비동기로 작동되며 결과는 `callback(filenames)`을 통해 전달됩니다.
|
||||
|
||||
Windows와 Linux에선 파일 선택 모드, 디렉터리 선택 모드를 동시에 사용할 수 없습니다.
|
||||
|
@ -72,7 +77,7 @@ Windows와 Linux에선 파일 선택 모드, 디렉터리 선택 모드를 동
|
|||
* `title` String - 대화 상자의 제목입니다. 몇몇 플랫폼에선 보이지 않을 수 있습니다.
|
||||
* `message` String - 대화 상자의 본문 내용입니다.
|
||||
* `detail` String - 메시지의 추가 정보입니다.
|
||||
* `icon` [NativeImage](native-image-ko.md)
|
||||
* `icon` [NativeImage](native-image.md)
|
||||
* `cancelId` Integer - 유저가 대화 상자의 버튼을 클릭하지 않고 대화 상자를 취소했을 때 반환되는 버튼의 index입니다.
|
||||
기본적으로 버튼 리스트가 "cancel" 또는 "no" 라벨을 가지고 있을 때 해당 버튼의 index를 반환합니다. 따로 두 라벨이 지정되지 않은 경우 0을 반환합니다.
|
||||
OS X와 Windows에선 `cancelId` 지정 여부에 상관없이 "Cancel" 버튼이 언제나 `cancelId`로 지정됩니다.
|
|
@ -4,7 +4,7 @@ Frameless 윈도우는 테두리가 없는 윈도우 창을 말합니다.
|
|||
|
||||
## Frameless 윈도우 만들기
|
||||
|
||||
Frameless 윈도우를 만드려면 [BrowserWindow](browser-window-ko.md) 객체의 `options`에서 `frame` 옵션을 `false`로 지정하기만 하면됩니다:
|
||||
Frameless 윈도우를 만드려면 [BrowserWindow](browser-window.md) 객체의 `options`에서 `frame` 옵션을 `false`로 지정하기만 하면됩니다:
|
||||
|
||||
```javascript
|
||||
var BrowserWindow = require('browser-window');
|
|
@ -32,20 +32,20 @@ app.on('will-quit', function() {
|
|||
|
||||
## globalShortcut.register(accelerator, callback)
|
||||
|
||||
* `accelerator` [Accelerator](accelerator-ko.md)
|
||||
* `accelerator` [Accelerator](accelerator.md)
|
||||
* `callback` Function
|
||||
|
||||
`accelerator`로 표현된 전역 단축키를 등록합니다. 유저로부터 등록된 단축키가 눌렸을 경우 `callback` 함수가 호출됩니다.
|
||||
|
||||
## globalShortcut.isRegistered(accelerator)
|
||||
|
||||
* `accelerator` [Accelerator](accelerator-ko.md)
|
||||
* `accelerator` [Accelerator](accelerator.md)
|
||||
|
||||
지정된 `accelerator` 단축키가 등록되었는지 여부를 확인합니다. 반환값은 boolean(true, false) 입니다.
|
||||
|
||||
## globalShortcut.unregister(accelerator)
|
||||
|
||||
* `accelerator` [Accelerator](accelerator-ko.md)
|
||||
* `accelerator` [Accelerator](accelerator.md)
|
||||
|
||||
`키코드`에 해당하는 전역 단축키를 등록 해제합니다.
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
동기 메시지는 `event.returnValue`를 이용하여 반환값(답장)을 설정할 수 있습니다. 비동기 메시지라면 `event.sender.send(...)`를 사용하면 됩니다.
|
||||
|
||||
또한 메인 프로세스에서 랜더러 프로세스로 메시지를 보내는 것도 가능합니다.
|
||||
자세한 내용은 [WebContents.send](browser-window-ko.md#webcontentssendchannel-args)를 참고 하세요.
|
||||
자세한 내용은 [WebContents.send](browser-window.md#webcontentssendchannel-args)를 참고 하세요.
|
||||
|
||||
보내진 메시지들을 처리하는 예제입니다:
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
# ipc (renderer)
|
||||
|
||||
`ipc` 모듈은 메인 프로세스로 메시지를 동기 또는 비동기로 보내고 받을 수 있는 몇 가지 방법을 제공합니다.
|
||||
만약 랜더러 프로세스에서 메인 프로세스의 모듈을 직접적으로 사용하고 싶다면 [remote](remote-ko.md) 모듈을 사용하는 것을 고려해보는 것이 좋습니다.
|
||||
만약 랜더러 프로세스에서 메인 프로세스의 모듈을 직접적으로 사용하고 싶다면 [remote](remote.md) 모듈을 사용하는 것을 고려해보는 것이 좋습니다.
|
||||
|
||||
[ipc (main process)](ipc-main-process-ko.md)에서 예제를 볼 수 있습니다.
|
||||
[ipc (main process)](ipc-main-process.md)에서 예제를 볼 수 있습니다.
|
||||
|
||||
## ipc.send(channel[, args...])
|
||||
|
|
@ -10,8 +10,8 @@
|
|||
* `type` String - `MenuItem`의 타입 `normal`, `separator`, `submenu`, `checkbox` 또는 `radio` 사용가능
|
||||
* `label` String
|
||||
* `sublabel` String
|
||||
* `accelerator` [Accelerator](accelerator-ko.md)
|
||||
* `icon` [NativeImage](native-image-ko.md)
|
||||
* `accelerator` [Accelerator](accelerator.md)
|
||||
* `icon` [NativeImage](native-image.md)
|
||||
* `enabled` Boolean
|
||||
* `visible` Boolean
|
||||
* `checked` Boolean
|
|
@ -1,10 +1,11 @@
|
|||
# menu
|
||||
|
||||
`Menu` 클래스는 어플리케이션 메뉴와 컨텍스트 메뉴를 만들 때 사용할 수 있습니다.
|
||||
각 메뉴는 여러 개의 메뉴 아이템으로 구성되어 있으며 서브 메뉴를 가질 수도 있습니다.
|
||||
`Menu` 클래스는 어플리케이션 메뉴와 컨텍스트 메뉴를 만들 때 사용됩니다.
|
||||
메뉴는 여러 개의 메뉴 아이템으로 구성되고 서브 메뉴를 가질 수도 있습니다.
|
||||
|
||||
다음 예제는 웹 페이지 내에서 [remote](remote-ko.md) 모듈을 활용하여 동적으로 메뉴를 생성하는 예제입니다.
|
||||
그리고 이 예제에서 만들어진 메뉴는 유저가 페이지에서 오른쪽 클릭을 할 때 마우스 위치에 팝업으로 표시됩니다:
|
||||
다음 예제는 웹 페이지 내에서 [remote](remote.md) 모듈을 활용하여 동적으로 메뉴를 생성하는 예제입니다.
|
||||
|
||||
이 예제에서 만들어진 메뉴는 유저가 페이지에서 오른쪽 클릭을 할 때 마우스 위치에 팝업 형태로 표시됩니다:
|
||||
|
||||
```html
|
||||
<!-- index.html -->
|
||||
|
@ -27,7 +28,7 @@ window.addEventListener('contextmenu', function (e) {
|
|||
|
||||
다음 예제는 template API를 활용하여 어플리케이션 메뉴를 만드는 간단한 예제입니다:
|
||||
|
||||
**Windows 와 Linux 주의:** 각 메뉴 아이템의 `selector` 멤버는 Mac 운영체제 전용입니다. [Accelerator 옵션](https://github.com/atom/electron/blob/master/docs/api/accelerator-ko.md)
|
||||
**Windows 와 Linux 유저 주의:** 각 메뉴 아이템의 `selector` 멤버는 Mac 운영체제 전용입니다. [Accelerator 옵션](accelerator.md)
|
||||
|
||||
```html
|
||||
<!-- index.html -->
|
||||
|
@ -188,7 +189,7 @@ Menu.setApplicationMenu(menu);
|
|||
|
||||
* `template` Array
|
||||
|
||||
기본적으로 `template`는 [MenuItem](menu-item-ko.md)을 생성할 때 사용하는 `options`의 배열입니다. 사용법은 위에서 설명한 것과 같습니다.
|
||||
기본적으로 `template`는 [MenuItem](menu-item.md)을 생성할 때 사용하는 `options`의 배열입니다. 사용법은 위에서 설명한 것과 같습니다.
|
||||
|
||||
또한 `template`에는 다른 속성도 추가할 수 있으며 메뉴가 만들어질 때 해당 메뉴 아이템의 프로퍼티로 변환됩니다.
|
||||
|
|
@ -21,6 +21,8 @@ var appIcon = new Tray(image);
|
|||
|
||||
현재 `PNG` 와 `JPEG` 포맷을 지원하고 있습니다. 손실 없는 이미지 압축과 투명도 지원을 위해 `PNG` 사용을 권장합니다.
|
||||
|
||||
그리고 Windows에서는 `ICO` 포맷도 사용할 수 있습니다.
|
||||
|
||||
## 고해상도 이미지
|
||||
|
||||
플랫폼이 high-DPI를 지원하는 경우 `@2x`와 같이 이미지의 파일명 뒤에 접미사를 추가하여 고해상도 이미지로 지정할 수 있습니다.
|
145
docs-translations/ko/api/protocol.md
Normal file
145
docs-translations/ko/api/protocol.md
Normal file
|
@ -0,0 +1,145 @@
|
|||
# protocol
|
||||
|
||||
`protocol` 모듈은 이미 있는 프로토콜의 동작을 가로채거나 새로운 프로토콜을 만들 수 있는 기능을 제공합니다.
|
||||
|
||||
다음 예제는 `file://` 프로토콜과 같은 일을 하는 커스텀 프로토콜을 설정합니다:
|
||||
|
||||
```javascript
|
||||
var app = require('app');
|
||||
var path = require('path');
|
||||
|
||||
app.on('ready', function() {
|
||||
var protocol = require('protocol');
|
||||
protocol.registerFileProtocol('atom', function(request, callback) {
|
||||
var url = request.url.substr(7);
|
||||
callback({path: path.normalize(__dirname + '/' + url)});
|
||||
}, function (error) {
|
||||
if (error)
|
||||
console.error('Failed to register protocol')
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
**알림:** 이 모듈은 `ready` 이벤트가 호출된 이후에만 사용할 수 있습니다.
|
||||
|
||||
## protocol.registerStandardSchemes(schemes)
|
||||
|
||||
* `schemes` Array - 표준 스킴으로 등록할 커스텀 스킴 리스트
|
||||
|
||||
표준 스킴의 형식은 RFC 3986 [일반 URI 구문](https://tools.ietf.org/html/rfc3986#section-3) 표준을 따릅니다.
|
||||
이 형식은 `file:`과 `filesystem:`을 포함합니다.
|
||||
|
||||
## protocol.registerFileProtocol(scheme, handler[, completion])
|
||||
|
||||
* `scheme` String
|
||||
* `handler` Function
|
||||
* `completion` Function
|
||||
|
||||
`scheme`에 파일을 응답으로 보내는 프로토콜을 등록합니다.
|
||||
`handler`는 `request`가 `scheme`와 함께 생성될 때 `handler(request, callback)` 형식으로 호출됩니다.
|
||||
`completion`은 `scheme`가 성공적으로 등록되었을 때 `completion(null)` 형식으로 호출되고
|
||||
등록에 실패했을 땐 `completion(error)` 형식으로 에러 내용을 담아 호출됩니다.
|
||||
|
||||
`request`를 처리할 때 반드시 파일 경로 또는 `path` 속성을 포함하는 객체를 인자에 포함하여 `callback`을 호출해야 합니다.
|
||||
예: `callback(filePath)` 또는 `callback({path: filePath})`.
|
||||
|
||||
만약 `callback`이 아무 인자도 없이 호출되거나 숫자나 `error` 프로퍼티를 가진 객체가 인자로 전달될 경우
|
||||
`request`는 지정한 `error` 코드(숫자)를 출력합니다.
|
||||
사용할 수 있는 에러 코드는 다음 링크에서 확인할 수 있습니다: https://code.google.com/p/chromium/codesearch#chromium/src/net/base/net_error_list.h
|
||||
|
||||
기본적으로 스킴은 `http:`와 같이 처리됩니다. `file:`과 같이 "일반적인 URI 문법"과는 다르게 인식되는 프로토콜은
|
||||
`protocol.registerStandardSchemes`을 사용하여 표준 스킴으로 처리되도록 할 수 있습니다.
|
||||
|
||||
## protocol.registerBufferProtocol(scheme, handler[, completion])
|
||||
|
||||
* `scheme` String
|
||||
* `handler` Function
|
||||
* `completion` Function
|
||||
|
||||
`scheme`에 `Buffer`를 응답으로 보내는 프로토콜을 등록합니다.
|
||||
반드시 `Buffer` 또는 `data`, `mimeType`, `chart` 속성을 포함한 객체 중 하나를 인자에 포함하여 `callback`을 호출해야 합니다.
|
||||
|
||||
예제:
|
||||
|
||||
```javascript
|
||||
protocol.registerBufferProtocol('atom', function(request, callback) {
|
||||
callback({mimeType: 'text/html', data: new Buffer('<h5>Response</h5>')});
|
||||
}, function (error) {
|
||||
if (error)
|
||||
console.error('Failed to register protocol')
|
||||
});
|
||||
```
|
||||
|
||||
## protocol.registerStringProtocol(scheme, handler[, completion])
|
||||
|
||||
* `scheme` String
|
||||
* `handler` Function
|
||||
* `completion` Function
|
||||
|
||||
`scheme`에 `문자열`를 응답으로 보내는 프로토콜을 등록합니다.
|
||||
반드시 `문자열` 또는 `data`, `mimeType`, `chart` 속성을 포함한 객체 중 하나를 인자에 포함하여 `callback`을 호출해야 합니다.
|
||||
|
||||
## protocol.registerHttpProtocol(scheme, handler[, completion])
|
||||
|
||||
* `scheme` String
|
||||
* `handler` Function
|
||||
* `completion` Function
|
||||
|
||||
|
||||
`scheme`에 HTTP 요청을 응답으로 보내는 프로토콜을 등록합니다.
|
||||
반드시 `url`, `method`, `referer`, `session` 속성을 포함하는 객체를 인자에 포함하여 `callback`을 호출해야 합니다.
|
||||
|
||||
기본적으로 HTTP 요청은 현재 세션을 재사용합니다. 만약 서로 다른 세션에 요청을 보내고 싶으면 `session`을 `null`로 지정해야 합니다.
|
||||
|
||||
## protocol.unregisterProtocol(scheme[, completion])
|
||||
|
||||
* `scheme` String
|
||||
* `completion` Function
|
||||
|
||||
`scheme`의 커스텀 프로토콜 등록을 해제합니다.
|
||||
|
||||
## protocol.isProtocolHandled(scheme, callback)
|
||||
|
||||
* `scheme` String
|
||||
* `callback` Function
|
||||
|
||||
`scheme`에 동작(handler)이 등록되어 있는지 여부를 확인합니다. `callback`으로 결과(boolean)가 반환됩니다.
|
||||
|
||||
## protocol.interceptFileProtocol(scheme, handler[, completion])
|
||||
|
||||
* `scheme` String
|
||||
* `handler` Function
|
||||
* `completion` Function
|
||||
|
||||
`scheme` 프로토콜을 가로채고 `handler`를 파일 전송에 대한 새로운 동작으로 사용합니다.
|
||||
|
||||
## protocol.interceptStringProtocol(scheme, handler[, completion])
|
||||
|
||||
* `scheme` String
|
||||
* `handler` Function
|
||||
* `completion` Function
|
||||
|
||||
`scheme` 프로토콜을 가로채고 `handler`를 문자열 전송에 대한 새로운 동작으로 사용합니다.
|
||||
|
||||
## protocol.interceptBufferProtocol(scheme, handler[, completion])
|
||||
|
||||
* `scheme` String
|
||||
* `handler` Function
|
||||
* `completion` Function
|
||||
|
||||
`scheme` 프로토콜을 가로채고 `handler`를 `Buffer` 전송에 대한 새로운 동작으로 사용합니다.
|
||||
|
||||
## protocol.interceptHttpProtocol(scheme, handler[, completion])
|
||||
|
||||
* `scheme` String
|
||||
* `handler` Function
|
||||
* `completion` Function
|
||||
|
||||
`scheme` 프로토콜을 가로채고 `handler`를 HTTP 프로토콜의 요청에 대한 새로운 동작으로 사용합니다.
|
||||
|
||||
## protocol.uninterceptProtocol(scheme[, completion])
|
||||
|
||||
* `scheme` String
|
||||
* `completion` Function
|
||||
|
||||
가로챈 `scheme`를 삭제하고 기본 핸들러로 복구합니다.
|
|
@ -70,7 +70,7 @@ remote.getCurrentWindow().on('close', function() {
|
|||
|
||||
## remote.getCurrentWindow()
|
||||
|
||||
현재 웹 페이지가 들어있는 [BrowserWindow](browser-window-ko.md) 객체를 반환합니다.
|
||||
현재 웹 페이지가 들어있는 [BrowserWindow](browser-window.md) 객체를 반환합니다.
|
||||
|
||||
## remote.getCurrentWebContents()
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
# 개요
|
||||
|
||||
Electron은 모든 [node.js's built-in 모듈](http://nodejs.org/api/)과 third-party node 모듈을 완벽하게 지원합니다. ([네이티브 모듈](../tutorial/using-native-node-modules-ko.md)을 포함해서)
|
||||
Electron은 모든 [node.js's built-in 모듈](http://nodejs.org/api/)과 third-party node 모듈을 완벽하게 지원합니다. ([네이티브 모듈](../tutorial/using-native-node-modules.md)을 포함해서)
|
||||
|
||||
Electron은 네이티브 데스크톱 어플리케이션을 개발 할 수 있도록 추가적인 built-in 모듈을 제공합니다.
|
||||
몇몇 모듈은 메인 프로세스에서만 사용할 수 있고 어떤 모듈은 랜더러 프로세스에서만 사용할 수 있습니다. 또한 두 프로세스 모두 사용할 수 있는 모듈도 있습니다.
|
||||
기본적인 규칙은 다음과 같습니다: GUI와 저 수준 시스템에 관련된 모듈은 오직 메인 프로세스에서만 사용할 수 있습니다.
|
||||
[메인 프로세스 vs. 랜더러 프로세스](../tutorial/quick-start-ko.md#메인 프로세스) 컨셉에 익숙해야 이 모듈들을 사용하기 쉬우므로 해당 문서를 정독하는 것을 권장합니다.
|
||||
[메인 프로세스 vs. 랜더러 프로세스](../tutorial/quick-start.md#메인 프로세스) 컨셉에 익숙해야 이 모듈들을 사용하기 쉬우므로 해당 문서를 정독하는 것을 권장합니다.
|
||||
|
||||
|
||||
메인 프로세스 스크립트는 일반 `node.js` 스크립트와 같습니다:
|
||||
|
@ -36,4 +36,4 @@ app.on('ready', function() {
|
|||
</html>
|
||||
```
|
||||
|
||||
어플리케이션을 실행하려면 [앱 실행하기](../tutorial/quick-start-ko.md#앱 실행하기) 문서를 참고하기 바랍니다.
|
||||
어플리케이션을 실행하려면 [앱 실행하기](../tutorial/quick-start.md#앱 실행하기) 문서를 참고하기 바랍니다.
|
|
@ -38,7 +38,7 @@ __플랫폼별 한계:__
|
|||
|
||||
### new Tray(image)
|
||||
|
||||
* `image` [NativeImage](native-image-ko.md)
|
||||
* `image` [NativeImage](native-image.md)
|
||||
|
||||
전달된 `image`를 이용하여 트레이 아이콘을 만듭니다.
|
||||
|
||||
|
@ -126,13 +126,13 @@ __주의:__ 이 기능은 OS X에서만 작동합니다.
|
|||
|
||||
### Tray.setImage(image)
|
||||
|
||||
* `image` [NativeImage](native-image-ko.md)
|
||||
* `image` [NativeImage](native-image.md)
|
||||
|
||||
`image`를 사용하여 트레이 아이콘의 이미지를 설정합니다.
|
||||
|
||||
### Tray.setPressedImage(image)
|
||||
|
||||
* `image` [NativeImage](native-image-ko.md)
|
||||
* `image` [NativeImage](native-image.md)
|
||||
|
||||
`image`를 사용하여 트레이 아이콘이 눌렸을 때의 이미지를 설정합니다.
|
||||
|
||||
|
@ -163,7 +163,7 @@ __주의:__ 이 기능은 OS X에서만 작동합니다.
|
|||
### Tray.displayBalloon(options)
|
||||
|
||||
* `options` Object
|
||||
* `icon` [NativeImage](native-image-ko.md)
|
||||
* `icon` [NativeImage](native-image.md)
|
||||
* `title` String
|
||||
* `content` String
|
||||
|
|
@ -311,7 +311,7 @@ Webview 페이지를 PDF 형식으로 인쇄합니다. `webContents.printToPDF(o
|
|||
`channel`을 통해 게스트 페이지에 `args...` 비동기 메시지를 보냅니다.
|
||||
게스트 페이지에선 `ipc` 모듈의 `channel` 이벤트를 사용하면 이 메시지를 받을 수 있습니다.
|
||||
|
||||
예제는 [WebContents.send](browser-window-ko.md#webcontentssendchannel-args)를 참고하세요.
|
||||
예제는 [WebContents.send](browser-window.md#webcontentssendchannel-args)를 참고하세요.
|
||||
|
||||
## DOM 이벤트
|
||||
|
|
@ -46,7 +46,7 @@ python script\build.py
|
|||
python script\build.py -c D
|
||||
```
|
||||
|
||||
빌드가 모두 끝나면 `out/D` 디렉터리에서 `atom.exe` 실행 파일을 찾을 수 있습니다.
|
||||
빌드가 모두 끝나면 `out/D` (디버그 타겟) 또는 `out/R` (릴리즈 타겟) 디렉터리에서 `electron.exe` 실행 파일을 찾을 수 있습니다.
|
||||
|
||||
## 64비트 빌드
|
||||
|
|
@ -4,8 +4,15 @@
|
|||
|
||||
C++과 Python스크립트는 Chromium의 [코딩 스타일](http://www.chromium.org/developers/coding-style)을 따릅니다.
|
||||
파이선 스크립트 `script/cpplint.py`를 사용하여 모든 파일이 해당 코딩스타일에 맞게 코딩 되었는지 확인할 수 있습니다.
|
||||
|
||||
파이선의 버전은 2.7을 사용합니다.
|
||||
|
||||
C++ 코드는 많은 Chromium의 추상화와 타입을 사용합니다. 그래서 이에 대해 잘 알고 있어야 합니다.
|
||||
이와 관련하여 시작하기 좋은 장소로 Chromium의 [Important Abstractions and Data Structures]
|
||||
(https://www.chromium.org/developers/coding-style/important-abstractions-and-data-structures) 문서가 있습니다.
|
||||
이 문서에선 몇가지 특별한 타입과 스코프 타입(스코프 밖으로 나가면 자동으로 메모리에서 할당을 해제합니다. 스마트 포인터로 보시면 됩니다),
|
||||
로깅 메커니즘 등을 언급하고 있습니다.
|
||||
|
||||
## CoffeeScript
|
||||
|
||||
CoffeeScript의 경우 GitHub의 [스타일 가이드](https://github.com/styleguide/javascript)를 따릅니다. 동시에 다음 규칙도 따릅니다:
|
78
docs-translations/ko/styleguide.md
Normal file
78
docs-translations/ko/styleguide.md
Normal file
|
@ -0,0 +1,78 @@
|
|||
# Electron Documentation Styleguide
|
||||
|
||||
Find the appropriate section for your task: [reading Electron documentation](#)
|
||||
or [writing Electron documentation](#).
|
||||
|
||||
## Writing Electron Documentation
|
||||
|
||||
These are the ways that we construct the Electron documentation.
|
||||
|
||||
- Maximum one `h1` title per page.
|
||||
- Use `bash` instead of `cmd` in code blocks (because of syntax highlighter).
|
||||
- Doc `h1` titles should match object name (i.e. `browser-window` ¡æ
|
||||
`BrowserWindow`).
|
||||
- Hyphen separated filenames, however, are fine.
|
||||
- No headers following headers, add at least a one-sentence description.
|
||||
- Methods headers are wrapped in `code` ticks.
|
||||
- Event headers are wrapped in single 'quotation' marks.
|
||||
- No nesting lists more than 2 levels (unfortunately because of markdown
|
||||
renderer).
|
||||
- Add section titles: Events, Class Methods and Instance Methods.
|
||||
- Use 'will' over 'would' when describing outcomes.
|
||||
- Events and methods are `h3` headers.
|
||||
- Optional arguments written as `function (required[, optional])`.
|
||||
- Optional arguments are denoted when called out in list.
|
||||
- Line length is 80-column wrapped.
|
||||
- Platform specific methods are noted in italics following method header.
|
||||
- ```### `method(foo, bar)` _OS X_```
|
||||
|
||||
## Reading Electron Documentation
|
||||
|
||||
Here are some tips for understanding Electron documentation syntax.
|
||||
|
||||
### Methods
|
||||
|
||||
An example of [method](https://developer.mozilla.org/en-US/docs/Glossary/Method)
|
||||
documentation:
|
||||
|
||||
---
|
||||
|
||||
`methodName(required[, optional]))`
|
||||
|
||||
* `require` String, **required**
|
||||
* `optional` Integer
|
||||
|
||||
---
|
||||
|
||||
The method name is followed by the arguments it takes. Optional arguments are
|
||||
notated by brackets surrounding the optional argument as well as the comma
|
||||
required if this optional argument follows another argument.
|
||||
|
||||
Below the method is more detailed information on each of the arguments. The type
|
||||
of argument is notated by either the common types: [`String`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String), [`Number`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number), [`Object`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object), [`Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)
|
||||
or a custom type like Electron's [`webContent`](api/web-content.md).
|
||||
|
||||
### Events
|
||||
|
||||
An example of [event](https://developer.mozilla.org/en-US/docs/Web/API/Event)
|
||||
documentation:
|
||||
|
||||
---
|
||||
|
||||
Event: 'wake-up'
|
||||
|
||||
Returns:
|
||||
|
||||
* `time` String
|
||||
|
||||
---
|
||||
|
||||
The event is a string that is used after a `.on` listener method. If it returns
|
||||
a value it and its type is noted below. If you were to listen and respond to
|
||||
this event it might look something like this:
|
||||
|
||||
```javascript
|
||||
Alarm.on('wake-up', function(time) {
|
||||
console.log(time)
|
||||
})
|
||||
```
|
|
@ -1,6 +1,6 @@
|
|||
# 어플리케이션 배포
|
||||
|
||||
Electron 어플리케이션을 배포할 때는 어플리케이션 폴더의 이름을 `app`으로 지정한 후 Electron 실행파일의 리소스 디렉터리에 집어넣어야합니다.
|
||||
Electron 어플리케이션을 배포하려면 어플리케이션 폴더 이름을 `app`으로 지정한 후 Electron 실행파일의 리소스 디렉터리에 집어넣기만 하면 됩니다.
|
||||
리소스 디렉터리는 OS X에선 `Electron.app/Contents/Resources/` Windows와 Linux에선 `resources/` 입니다.
|
||||
|
||||
예제:
|
||||
|
@ -23,8 +23,8 @@ electron/resources/app
|
|||
└── index.html
|
||||
```
|
||||
|
||||
그리고 `Electron.app`을 실행하면(Linux에선 `electron` Windows에선 `electron.exe`입니다) Electron은 해당 앱을 실행시킵니다.
|
||||
최종 사용자에게는 이 `electron` 폴더(Electron.app)를 배포하면 됩니다.
|
||||
그리고 `Electron.app`을 실행하면(Linux에선 `electron` Windows에선 `electron.exe`입니다) Electron 앱이 실행시킵니다.
|
||||
최종 사용자에겐 이 `electron` 폴더(Electron.app)를 배포하면 됩니다.
|
||||
|
||||
## asar로 앱 패키징 하기
|
||||
|
||||
|
@ -48,7 +48,7 @@ electron/resources/
|
|||
└── app.asar
|
||||
```
|
||||
|
||||
자세한 내용은 [어플리케이션 패키징](application-packaging-ko.md)에서 찾아볼 수 있습니다.
|
||||
자세한 내용은 [어플리케이션 패키징](application-packaging.md)에서 찾아볼 수 있습니다.
|
||||
|
||||
## 다운로드한 바이너리의 리소스를 앱에 맞게 수정하기
|
||||
|
||||
|
@ -101,7 +101,7 @@ MyApp.app/Contents
|
|||
|
||||
### 역주-자동화
|
||||
|
||||
배포시에 Electron의 리소스를 일일이 수정하는 것은 매우 귀찮고 복잡합니다.
|
||||
어플리케이션 배포시 Electron의 리소스를 일일이 수정하는 것은 매우 귀찮고 복잡합니다.
|
||||
하지만 이 작업을 자동화 시킬 수 있는 몇가지 방법이 있습니다:
|
||||
|
||||
* [electron-builder](https://github.com/loopline-systems/electron-builder)
|
||||
|
@ -110,6 +110,7 @@ MyApp.app/Contents
|
|||
## Electron 소스코드를 다시 빌드하여 리소스 수정하기
|
||||
|
||||
또한 Electron 소스코드를 다시 빌드할 때 어플리케이션 이름을 변경할 수 있습니다.
|
||||
|
||||
`GYP_DEFINES` 환경변수를 사용하여 다음과 같이 다시 빌드할 수 있습니다:
|
||||
|
||||
__Windows__
|
||||
|
@ -133,6 +134,6 @@ $ script/build.py -c Release -t myapp
|
|||
### grunt-build-atom-shell
|
||||
|
||||
Electron의 소스코드를 수정하고 다시 빌드하는 작업은 상당히 복잡합니다.
|
||||
이를 해결하기 위해 [grunt-build-atom-shell](https://github.com/paulcbetts/grunt-build-atom-shell)를 사용하여 빌드를 자동화 시킬 수 있습니다.
|
||||
일일이 소스코드를 수정하는 대신 [grunt-build-atom-shell](https://github.com/paulcbetts/grunt-build-atom-shell)을 사용하여 빌드를 자동화 시킬 수 있습니다.
|
||||
|
||||
이 툴을 사용하면 자동으로 `.gyp`파일을 수정하고 다시 빌드합니다. 그리고 어플리케이션의 네이티브 Node 모듈 또한 새로운 실행파일 이름으로 매치 시킵니다.
|
|
@ -1,11 +1,11 @@
|
|||
# 어플리케이션 패키징
|
||||
|
||||
Windows에서 일어나는 긴 경로 이름에 대한 [issues](https://github.com/joyent/node/issues/6960)를 완화하고 `require` 속도를 약간 빠르게 하며
|
||||
어플리케이션의 리소스와 소스코드를 유저로부터 보호하기 위해 어플리케이션을 [asar][asar] 아카이브로 패키징 할 수 있습니다.
|
||||
어플리케이션의 리소스와 소스코드를 좋지 않은 사용자로부터 보호하기 위해 어플리케이션을 [asar][asar] 아카이브로 패키징 할 수 있습니다.
|
||||
|
||||
## `asar` 아카이브 생성
|
||||
|
||||
[asar][asar]아카이브는 tar과 비슷한 포맷으로 모든 리소스를 하나의 파일로 만듭니다.
|
||||
[asar][asar] 아카이브는 tar과 비슷한 포맷으로 모든 리소스를 하나의 파일로 만듭니다.
|
||||
그리고 Electron은 압축해제 없이 임의로 모든 파일을 읽어들일 수 있습니다.
|
||||
|
||||
다음 몇가지 단계를 통해 어플리케이션을 `asar` 아카이브로 압축할 수 있습니다:
|
||||
|
@ -29,7 +29,7 @@ Electron은 Node.js로 부터 제공된 Node API와 Chromium으로부터 제공
|
|||
|
||||
### Node API
|
||||
|
||||
`fs.readFile` 와 `require` 같은 Node API들을 지원하기 위해 Electron에선 `asar` 아카이브가 가상의 디렉터리 구조를 가지도록
|
||||
Electron에선 `fs.readFile`과 `require` 같은 Node API들을 지원하기 위해 `asar` 아카이브가 가상의 디렉터리 구조를 가지도록
|
||||
패치했습니다. 그래서 아카이브 내부에서 리소스들을 정상적인 파일 시스템처럼 접근할 수 있습니다.
|
||||
|
||||
예를 들어 `/path/to`라는 경로에 `example.asar`라는 아카이브가 있다고 가정하면:
|
|
@ -211,12 +211,12 @@ window.setRepresentedFilename('/etc/passwd');
|
|||
window.setDocumentEdited(true);
|
||||
```
|
||||
|
||||
[addrecentdocument]: ../api/app-ko.md#appaddrecentdocumentpath
|
||||
[clearrecentdocuments]: ../api/app-ko.md#appclearrecentdocuments
|
||||
[setusertaskstasks]: ../api/app-ko.md#appsetusertaskstasks
|
||||
[setprogressbar]: ../api/browser-window-ko.md#browserwindowsetprogressbarprogress
|
||||
[setrepresentedfilename]: ../api/browser-window-ko.md#browserwindowsetrepresentedfilenamefilename
|
||||
[setdocumentedited]: ../api/browser-window-ko.md#browserwindowsetdocumenteditededited
|
||||
[addrecentdocument]: ../api/app.md#appaddrecentdocumentpath
|
||||
[clearrecentdocuments]: ../api/app.md#appclearrecentdocuments
|
||||
[setusertaskstasks]: ../api/app.md#appsetusertaskstasks
|
||||
[setprogressbar]: ../api/browser-window.md#browserwindowsetprogressbarprogress
|
||||
[setrepresentedfilename]: ../api/browser-window.md#browserwindowsetrepresentedfilenamefilename
|
||||
[setdocumentedited]: ../api/browser-window.md#browserwindowsetdocumenteditededited
|
||||
[app-registration]: http://msdn.microsoft.com/en-us/library/windows/desktop/ee872121(v=vs.85).aspx
|
||||
[unity-launcher]: https://help.ubuntu.com/community/UnityLaunchersAndDesktopFiles#Adding_shortcuts_to_a_launcher
|
||||
[setthumbarbuttons]: ../api/browser-window.md#browserwindowsetthumbarbuttonsbuttons
|
|
@ -2,17 +2,16 @@
|
|||
|
||||
## 소개
|
||||
|
||||
Electron은 자바스크립트와 함께 제공되는 풍부한 네이티브 API를 이용하여 데스크톱 어플리케이션을 만들 수 있도록 해주는 프레임워크입니다.
|
||||
이 프레임워크의 io.js(node.js)는 웹 서버 개발이 아닌 데스크톱 어플리케이션 개발에 초점을 맞췄습니다.
|
||||
Electron은 자바스크립트와 함께 제공된 풍부한 네이티브 API를 사용하여 멋진 데스크탑 어플리케이션을 만들 수 있도록 해주는 프레임워크입니다.
|
||||
이 프레임워크의 io.js(node.js)는 웹 서버 개발이 아닌 데스크탑 어플리케이션 개발에 초점을 맞췄습니다.
|
||||
|
||||
이것은 Electron이 GUI 라이브러리의 자바스크립트 바인딩이라는 뜻이 아닙니다.
|
||||
대신에, Electron은 웹 페이지의 GUI를 사용합니다. 쉽게 말해 Electron은 자바스크립트를 사용하여 조작하는 작은 Chromium
|
||||
브라우저로 볼 수 있습니다.
|
||||
이 말은 Electron이 GUI 라이브러리의 자바스크립트 바인딩이라는 뜻이 아닙니다.
|
||||
대신, Electron은 웹 페이지의 GUI를 사용합니다. 쉽게 말해 Electron은 자바스크립트를 사용하여 조작하는 작은 Chromium 브라우저로 볼 수 있습니다.
|
||||
|
||||
### 메인 프로세스
|
||||
|
||||
Electron은 실행될 때 __메인 프로세스__ 로 불리는 `package.json`의 `main` 스크립트를 호출합니다.
|
||||
이 스크립트는 메인 프로세스에서 작동합니다. GUI 컴포넌트를 컨트롤하거나 웹 페이지 창을 생성할 수 있습니다.
|
||||
이 스크립트는 메인 프로세스에서 작동합니다. GUI 컴포넌트를 조작하거나 웹 페이지 창을 생성할 수 있습니다.
|
||||
|
||||
### 랜더러 프로세스
|
||||
|
||||
|
@ -31,11 +30,11 @@ Electron 프로세스 내에서 작동하는 웹 페이지는 __랜더러 프로
|
|||
랜더러 프로세스는 각각의 프로세스에 고립되며 웹 페이지의 작동에만 영향을 끼칩니다.
|
||||
|
||||
웹 페이지 내에서 네이티브 GUI 리소스를 관리하는 것은 보안에 취약하고 리소스를 누수시킬 수 있기 때문에
|
||||
웹 페이지 내에서는 네이티브 GUI와 관련된 API를 호출할 수 없도록 되어 있습니다.
|
||||
기본적으로 웹 페이지 내에서는 네이티브 GUI와 관련된 API를 호출할 수 없도록 되어 있습니다.
|
||||
만약 웹 페이지 내에서 GUI작업이 필요하다면 메인 프로세스에서 그 작업을 할 수 있도록 메인 프로세스와 통신을 해야합니다.
|
||||
|
||||
Electron에는 메인 프로세스와 랜더러 프로세스간에 통신을 할 수 있도록 [ipc](../api/ipc-renderer-ko.md) 모듈을 제공하고 있습니다.
|
||||
또한 [remote](../api/remote-ko.md) 모듈을 사용하여 RPC 스타일로 통신할 수도 있습니다.
|
||||
Electron에는 메인 프로세스와 랜더러 프로세스간에 통신을 할 수 있도록 [ipc](../api/ipc-renderer.md) 모듈을 제공하고 있습니다.
|
||||
또한 [remote](../api/remote.md) 모듈을 사용하여 RPC 스타일로 통신할 수도 있습니다.
|
||||
|
||||
## 첫번째 Electron 앱 만들기
|
||||
|
||||
|
@ -48,8 +47,8 @@ your-app/
|
|||
└── index.html
|
||||
```
|
||||
|
||||
`package.json`은 node 모듈의 package.json과 같습니다. 그리고 `main` 필드를 지정하여
|
||||
메인 프로세스로 사용할 어플리케이션 시작점을 정의할 수 있습니다.
|
||||
`package.json`은 node 모듈의 package.json과 같습니다.
|
||||
그리고 `main` 필드에 스크립트 파일을 집어넣어 메인 프로세스로 사용할 엔트리 포인트를 지정할 수 있습니다.
|
||||
예를 들어 사용할 수 있는 `package.json`은 다음과 같습니다:
|
||||
|
||||
```json
|
||||
|
@ -60,7 +59,9 @@ your-app/
|
|||
}
|
||||
```
|
||||
|
||||
`main.js`에서 창을 만들거나 시스템 이벤트를 처리할 수 있습니다. 대표적인 예제로 다음과 같이 작성할 수 있습니다:
|
||||
__알림__: 만약 `main` 필드가 `package.json`에 설정되어 있지 않으면 Electron은 자동으로 같은 디렉터리의 `index.js`를 로드합니다.
|
||||
|
||||
반드시 `main.js`에서 창을 만들고 시스템 이밴트를 처리해야합니다. 대표적인 예제로 다음과 같이 작성할 수 있습니다:
|
||||
|
||||
```javascript
|
||||
var app = require('app'); // 어플리케이션 기반을 조작 하는 모듈.
|
||||
|
@ -104,7 +105,7 @@ app.on('ready', function() {
|
|||
});
|
||||
```
|
||||
|
||||
마지막으로 사용자에게 보여줄 `index.html` 웹 페이지의 예제입니다:
|
||||
마지막으로, 사용자에게 보여줄 `index.html` 웹 페이지의 예제입니다:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
|
@ -122,7 +123,7 @@ app.on('ready', function() {
|
|||
|
||||
## 앱 실행하기
|
||||
|
||||
앱을 작성한 후 [어플리케이션 배포](./application-distribution-ko.md) 가이드를 따라 앱을 패키징 하고 패키징한 앱을 실행할 수 있습니다.
|
||||
앱을 작성한 후 [어플리케이션 배포](application-distribution.md) 가이드를 따라 앱을 패키징 하고 패키징한 앱을 실행할 수 있습니다.
|
||||
또는 Electron 실행파일을 다운로드 받아 바로 실행해 볼 수도 있습니다.
|
||||
|
||||
### electron-prebuilt
|
||||
|
@ -169,9 +170,9 @@ $ ./electron/electron your-app/
|
|||
$ ./Electron.app/Contents/MacOS/Electron your-app/
|
||||
```
|
||||
|
||||
앱 실행파일은 `Electron`의 release 패키지에 포함되어 있습니다.
|
||||
어플리케이션 실행파일은 `Electron`의 release 패키지에 포함되어 있습니다.
|
||||
[여기](https://github.com/atom/electron/releases)에서 다운로드 받을 수 있습니다.
|
||||
|
||||
### 배포용 파일 만들기
|
||||
|
||||
모든 앱 작성이 끝났다면 [어플리케이션 배포](./application-distribution-ko.md) 가이드를 보고 본격적으로 제작한 앱을 배포할 수 있습니다.
|
||||
어플리케이션 작성을 완료했다면 [어플리케이션 배포](application-distribution.md) 가이드를 통해 본격적으로 제작한 앱을 배포할 수 있습니다.
|
|
@ -29,7 +29,7 @@ Starting ChromeDriver (v2.10.291558) on port 9515
|
|||
Only local connections are allowed.
|
||||
```
|
||||
|
||||
곧 사용하므로 포트 `9515`를 기억해 놓습니다.
|
||||
포트 `9515`는 나중에 사용하므로 기억해 놓읍시다
|
||||
|
||||
### 2. WebDriverJS 설치
|
||||
|
||||
|
@ -66,9 +66,56 @@ driver.wait(function() {
|
|||
driver.quit();
|
||||
```
|
||||
|
||||
## WebdriverIO 설정하기
|
||||
|
||||
[WebdriverIO](http://webdriver.io/)는 웹 드라이버와 함께 테스트를 위해 제공되는 node 패키지입니다.
|
||||
|
||||
### 1. 크롬 드라이버 시작
|
||||
|
||||
먼저, `chromedriver` 바이너리를 다운로드 받고 실행합니다:
|
||||
|
||||
```bash
|
||||
$ chromedriver --url-base=/wd/hub --port=9515
|
||||
Starting ChromeDriver (v2.10.291558) on port 9515
|
||||
Only local connections are allowed.
|
||||
```
|
||||
|
||||
포트 `9515`는 나중에 사용하므로 기억해 놓읍시다
|
||||
|
||||
### 2. WebDriverIO 설치
|
||||
|
||||
```bash
|
||||
$ npm install webdriverio
|
||||
```
|
||||
|
||||
### 3. 크롬 드라이버에 연결
|
||||
```javascript
|
||||
var webdriverio = require('webdriverio');
|
||||
var options = {
|
||||
host: "localhost", // Use localhost as chrome driver server
|
||||
port: 9515, // "9515" is the port opened by chrome driver.
|
||||
desiredCapabilities: {
|
||||
browserName: 'chrome',
|
||||
chromeOptions: {binary: '/Path-to-Your-App.app/Electron'} // Path to your Electron binary.
|
||||
}
|
||||
};
|
||||
|
||||
var client = webdriverio.remote(options);
|
||||
|
||||
client
|
||||
.init()
|
||||
.url('http://google.com')
|
||||
.setValue('#q', 'webdriverio')
|
||||
.click('#btnG')
|
||||
.getTitle().then(function(title) {
|
||||
console.log('Title was: ' + title);
|
||||
})
|
||||
.end();
|
||||
```
|
||||
|
||||
## 작업환경
|
||||
|
||||
따로 Electron을 다시 빌드하지 않는 경우 간단히 어플리케이션을 Electron의 리소스 디렉터리에
|
||||
[배치](https://github.com/atom/electron/blob/master/docs/tutorial/application-distribution-ko.md)하여 바로 테스트 할 수 있습니다.
|
||||
[배치](application-distribution.md)하여 바로 테스트 할 수 있습니다.
|
||||
|
||||
[chrome-driver]: https://sites.google.com/a/chromium.org/chromedriver/
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue