Merge pull request #1 from atom/master

Update from original
This commit is contained in:
Eran Tiktin 2015-08-27 20:31:20 +03:00
commit b7d80e792d
119 changed files with 1967 additions and 755 deletions

View file

@ -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) 프로젝트엔 커뮤니티가 운영중인 유용한 예제 어플리케이션과 도구, 리소스가 있으니 한번 참고해 보시기 바랍니다.

View file

@ -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)

View file

@ -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);

View file

@ -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.

View file

@ -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...);
}

View file

@ -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() {

View file

@ -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());

View file

@ -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_;

View file

@ -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 {

View file

@ -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(

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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(

View file

@ -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(

View 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

View 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_

View file

@ -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>

View file

@ -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);
}

View file

@ -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())];

View file

@ -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);

View file

@ -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.

View file

@ -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)

View file

@ -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_

View file

@ -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);

View file

@ -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

View file

@ -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);
};

View file

@ -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

View file

@ -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_;

View file

@ -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());

View file

@ -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(

View file

@ -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");
}

View file

@ -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",

View file

@ -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(

View file

@ -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

View file

@ -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 = ->

View file

@ -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

View file

@ -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_

View 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

View file

@ -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_

View 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)

View file

@ -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`를 호출해야 합니다.

View file

@ -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

View file

@ -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`를 씁니다.

View file

@ -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.

View file

@ -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`로 지정됩니다.

View file

@ -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');

View file

@ -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)
`키코드`에 해당하는 전역 단축키를 등록 해제합니다.

View file

@ -6,7 +6,7 @@
동기 메시지는 `event.returnValue`를 이용하여 반환값(답장)을 설정할 수 있습니다. 비동기 메시지라면 `event.sender.send(...)`를 사용하면 됩니다.
또한 메인 프로세스에서 랜더러 프로세스로 메시지를 보내는 것도 가능합니다.
자세한 내용은 [WebContents.send](browser-window-ko.md#webcontentssendchannel-args)를 참고 하세요.
자세한 내용은 [WebContents.send](browser-window.md#webcontentssendchannel-args)를 참고 하세요.
보내진 메시지들을 처리하는 예제입니다:

View file

@ -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...])

View file

@ -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

View file

@ -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`에는 다른 속성도 추가할 수 있으며 메뉴가 만들어질 때 해당 메뉴 아이템의 프로퍼티로 변환됩니다.

View file

@ -21,6 +21,8 @@ var appIcon = new Tray(image);
현재 `PNG``JPEG` 포맷을 지원하고 있습니다. 손실 없는 이미지 압축과 투명도 지원을 위해 `PNG` 사용을 권장합니다.
그리고 Windows에서는 `ICO` 포맷도 사용할 수 있습니다.
## 고해상도 이미지
플랫폼이 high-DPI를 지원하는 경우 `@2x`와 같이 이미지의 파일명 뒤에 접미사를 추가하여 고해상도 이미지로 지정할 수 있습니다.

View 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`를 삭제하고 기본 핸들러로 복구합니다.

View file

@ -70,7 +70,7 @@ remote.getCurrentWindow().on('close', function() {
## remote.getCurrentWindow()
현재 웹 페이지가 들어있는 [BrowserWindow](browser-window-ko.md) 객체를 반환합니다.
현재 웹 페이지가 들어있는 [BrowserWindow](browser-window.md) 객체를 반환합니다.
## remote.getCurrentWebContents()

View file

@ -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#앱 실행하기) 문서를 참고하기 바랍니다.

View file

@ -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

View file

@ -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 이벤트

View file

@ -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비트 빌드

View file

@ -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)를 따릅니다. 동시에 다음 규칙도 따릅니다:

View 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)
})
```

View file

@ -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 모듈 또한 새로운 실행파일 이름으로 매치 시킵니다.

View file

@ -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`라는 아카이브가 있다고 가정하면:

View file

@ -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

View file

@ -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) 가이드를 통해 본격적으로 제작한 앱을 배포할 수 있습니다.

View file

@ -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