# App {: .doctitle}

---

[TOC]

## App.argv

Get the filtered command line arguments when starting the app. In NW.js, some command line arguments are used by NW.js, which should not be interested of your app. `App.argv` will filter out those arguments and return the ones left. You can get filtered patterns from `App.filteredArgv` and the full arguments from `App.fullArgv`.

## App.fullArgv

Get all the command line arguments when starting the app. The return values contains the arguments used by NW.js, such as `--nwapp`, `--remote-debugging-port` etc.

## App.filteredArgv

Get a list of patterns of filtered command line arguments used by `App.argv`. By default, following patterns are used to filter the arguments:

```javascript
[
  /^--url=/,
  /^--remote-debugging-port=/,
  /^--renderer-cmd-prefix=/,
  /^--nwapp=/
]
```

## App.startPath

Get the directory where the application starts. The application will change the current directory to where the package files reside after start.

## App.dataPath

Get the application's data path in user's directory.

* Windows: `%LOCALAPPDATA%/<name>`
* Linux: `~/.config/<name>`
* OS X: `~/Library/Application Support/<name>/Default` (was `~/Library/Application Support/<name>` in v0.12.3 and below)

`<name>` is the **name** field in the `package.json` manifest.

## App.manifest

Get the JSON object of the manifest file.

## App.clearCache()

Clear the HTTP cache in memory and the one on disk. This method call is synchronized.

## App.clearAppCache(manifest_url)

Mark the Application cache group specified by manifest_url obsolete. This method call is synchronized.

## App.closeAllWindows()

Send the `close` event to all windows of current app, if no window is blocking the `close` event, then the app will quit after all windows have done shutdown. Use this method to quit an app will give windows a chance to save data.

## App.crashBrowser()
## App.crashRenderer()

These 2 functions crashes the browser process and the renderer process respectively, to test the [Crash dump](../For Developers/Understanding Crash Dump.md) feature.

## App.getProxyForURL(url)

* `url` `{String}` the URL to query for proxy

Query the proxy to be used for loading `url` in DOM. The return value is in the same format used in [PAC](http://en.wikipedia.org/wiki/Proxy_auto-config) (e.g. "DIRECT", "PROXY localhost:8080").

## App.setProxyConfig(config, pac_url)

* `config` `{String}` Proxy rules
* `pac_url` `{String}` PAC url

Set the proxy config which the web engine will be used to request network resources or PAC url to detect proxy automatically.

Rule (copied from [`net/proxy/proxy_config.h`](https://github.com/nwjs/chromium.src/blob/nw13/net/proxy/proxy_config.h))

```
    // Parses the rules from a string, indicating which proxies to use.
    //
    //   proxy-uri = [<proxy-scheme>"://"]<proxy-host>[":"<proxy-port>]
    //
    //   proxy-uri-list = <proxy-uri>[","<proxy-uri-list>]
    //
    //   url-scheme = "http" | "https" | "ftp" | "socks"
    //
    //   scheme-proxies = [<url-scheme>"="]<proxy-uri-list>
    //
    //   proxy-rules = scheme-proxies[";"<scheme-proxies>]
    //
    // Thus, the proxy-rules string should be a semicolon-separated list of
    // ordered proxies that apply to a particular URL scheme. Unless specified,
    // the proxy scheme for proxy-uris is assumed to be http.
    //
    // Some special cases:
    //  * If the scheme is omitted from the first proxy list, that list applies
    //    to all URL schemes and subsequent lists are ignored.
    //  * If a scheme is omitted from any proxy list after a list where a scheme
    //    has been provided, the list without a scheme is ignored.
    //  * If the url-scheme is set to 'socks', that sets a fallback list that
    //    to all otherwise unspecified url-schemes, however the default proxy-
    //    scheme for proxy urls in the 'socks' list is understood to be
    //    socks4:// if unspecified.
    //
    // For example:
    //   "http=foopy:80;ftp=foopy2"  -- use HTTP proxy "foopy:80" for http://
    //                                  URLs, and HTTP proxy "foopy2:80" for
    //                                  ftp:// URLs.
    //   "foopy:80"                  -- use HTTP proxy "foopy:80" for all URLs.
    //   "foopy:80,bar,direct://"    -- use HTTP proxy "foopy:80" for all URLs,
    //                                  failing over to "bar" if "foopy:80" is
    //                                  unavailable, and after that using no
    //                                  proxy.
    //   "socks4://foopy"            -- use SOCKS v4 proxy "foopy:1080" for all
    //                                  URLs.
    //   "http=foop,socks5://bar.com -- use HTTP proxy "foopy" for http URLs,
    //                                  and fail over to the SOCKS5 proxy
    //                                  "bar.com" if "foop" is unavailable.
    //   "http=foopy,direct://       -- use HTTP proxy "foopy" for http URLs,
    //                                  and use no proxy if "foopy" is
    //                                  unavailable.
    //   "http=foopy;socks=foopy2   --  use HTTP proxy "foopy" for http URLs,
    //                                  and use socks4://foopy2 for all other
    //                                  URLs.
```

## App.quit()

Quit current app. This method will **not** send `close` event to windows and app will just quit quietly.

## App.setCrashDumpDir(dir)

!!! warning "Deprecated"
    This API was deprecated since 0.11.0.

* `dir` `{String}` path to generate the crash dump

Set the directory where the minidump file will be saved on crash. For more information, see [Crash dump](../For Developers/Understanding Crash Dump.md).

## App.addOriginAccessWhitelistEntry(sourceOrigin, destinationProtocol, destinationHost, allowDestinationSubdomains)

* `sourceOrigin` `{String}` The source origin. e.g. `http://github.com/`
* `destinationProtocol` `{String}` The destination protocol where the `sourceOrigin` can access to. e.g. `app`
* `destinationHost` `{String}` The destination host where the `sourceOrigin` can access to. e.g. `myapp`
* `allowDestinationSubdomains` `{Boolean}` If set to true, the `sourceOrigin` is allowed to access subdomains of destinations.

Add an entry to the whitelist used for controlling cross-origin access. Suppose you want to allow HTTP redirecting from `github.com` to the page of your app, use something like this:

```javascript
App.addOriginAccessWhitelistEntry('http://github.com/', 'chrome-extension', location.host, true);
```

Use `App.removeOriginAccessWhitelistEntry` with exactly the same arguments to do the contrary.

## App.removeOriginAccessWhitelistEntry(sourceOrigin, destinationProtocol, destinationHost, allowDestinationSubdomains)

* `sourceOrigin` `{String}` The source origin. e.g. `http://github.com/`
* `destinationProtocol` `{String}` The destination protocol where the `sourceOrigin` can access to. e.g. `app`
* `destinationHost` `{String}` The destination host where the `sourceOrigin` can access to. e.g. `myapp`
* `allowDestinationSubdomains` `{Boolean}` If set to true, the `sourceOrigin` is allowed to access subdomains of destinations.

Remove an entry from the whitelist used for controlling cross-origin access. See `addOriginAccessWhitelistEntry` above.

## App.registerGlobalHotKey(shortcut)

* `shortcut` `{Shortcut}` the `Shortcut` object to register.

Register a global keyboard shortcut (also known as system-wide hot key) to the system.

See [Shortcut](Shortcut.md) for more information.

## App.unregisterGlobalHotKey(shortcut)

* `shortcut` `{Shortcut}` the `Shortcut` object to unregister.

Unregisters a global keyboard shortcut.

See [Shortcut](Shortcut.md) for more information.

## Event: open(args)

* `args` `{String}` the full command line of the program.

Emitted when users opened a file with your app.

## Event: reopen

This is a Mac specific feature. This event is sent when the user clicks the dock icon for an already running application.


// ref in chrome/browser/app_controller_mac.mm

CONTENT_EXPORT bool ApplicationShouldHandleReopenHook(bool hasVisibleWindows);

canhuang

v1.0.0


// ref in extensions/browser/api/app_window/app_window_api.cc

CONTENT_EXPORT void SetPinningRenderer(bool pin);

// ref in content/nw/src/api/tray/tray_aura.cc

CONTENT_EXPORT bool GetImage(Package* package, const base::FilePath& icon_path, gfx::Image* image);

#define INSIDE_BLINK 1
#include "third_party/blink/public/platform/web_rect.h"
#undef INSIDE_BLINK

#include "nw_extensions_renderer_hooks.h"
#include "content/public/common/content_features.h"
// nw
#include "content/nw/src/nw_version.h"

// base
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/i18n/icu_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread.h"
#include "base/values.h"
#include "ui/base/resource/resource_bundle.h"

// content
#include "content/public/renderer/v8_value_converter.h"
#include "content/public/common/content_switches.h"
#include "content/public/renderer/render_view.h"
#include "content/renderer/render_view_impl.h"

#include "content/common/frame.mojom.h"

// extensions
#include "extensions/renderer/dispatcher.h"
#include "extensions/renderer/renderer_extension_registry.h"
#include "extensions/renderer/script_context.h"
#include "extensions/common/extension.h"
#include "extensions/common/manifest.h"
#include "extensions/common/manifest_constants.h"
#include "extensions/renderer/native_extension_bindings_system.h"
#include "extensions/common/extension_features.h"

#include "extensions/grit/extensions_renderer_resources.h"

// third_party/blink/
#include "third_party/blink/public/web/web_document.h"
#include "third_party/blink/public/web/web_local_frame.h"
#include "third_party/blink/public/web/web_script_source.h"
#include "third_party/blink/public/web/web_view.h"
#include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h"
#include "third_party/blink/public/web/blink.h"
#include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
#include "third_party/blink/renderer/platform/wtf/casting.h"
#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"

#include "third_party/node-nw/src/node_webkit.h"

// gen
#include "nw/id/commit.h"

// NEED TO STAY SYNC WITH NODE
#ifndef NODE_CONTEXT_EMBEDDER_DATA_INDEX
#define NODE_CONTEXT_EMBEDDER_DATA_INDEX 32
#endif

#if defined(OS_WIN)
#define _USE_MATH_DEFINES
#include <math.h>
#endif

using content::RenderViewImpl;

using extensions::ScriptContext;
using extensions::Extension;
using extensions::RendererExtensionRegistry;
using extensions::Dispatcher;

using blink::WebScriptSource;

namespace manifest_keys = extensions::manifest_keys;

#if defined(COMPONENT_BUILD) && defined(WIN32)
#define NW_HOOK_MAP(type, sym, fn) BASE_EXPORT type fn;
#define BLINK_HOOK_MAP(type, sym, fn) BLINK_EXPORT type fn;
#define PLATFORM_HOOK_MAP(type, sym, fn) PLATFORM_EXPORT type fn;
#else
#define NW_HOOK_MAP(type, sym, fn) extern type fn;
#define BLINK_HOOK_MAP(type, sym, fn) extern type fn;
#define PLATFORM_HOOK_MAP(type, sym, fn) extern type fn;
#endif
#include "content/nw/src/common/node_hooks.h"
#undef NW_HOOK_MAP

namespace nw {

namespace {
std::string g_extension_id, g_extension_root;
extensions::Dispatcher* g_dispatcher = NULL;
bool g_skip_render_widget_hidden = false;

static inline v8::Local<v8::String> v8_str(const char* x) {
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  return v8::String::NewFromUtf8(isolate, x, v8::NewStringType::kNormal).ToLocalChecked();
}

v8::Handle<v8::Value> CallNWTickCallback(void* env, const v8::Handle<v8::Value> ret) {
  v8::MicrotasksScope microtasks(v8::Isolate::GetCurrent(), v8::MicrotasksScope::kDoNotRunMicrotasks);
  g_call_tick_callback_fn(env);
  return Undefined(v8::Isolate::GetCurrent());
}

v8::Handle<v8::Value> CreateNW(ScriptContext* context,
                               v8::Handle<v8::Object> node_global,
                               v8::Handle<v8::Context> node_context) {
  v8::Handle<v8::String> nw_string(
                                   v8::String::NewFromUtf8(context->isolate(), "nw", v8::NewStringType::kNormal).ToLocalChecked());
  v8::Handle<v8::Object> global(context->v8_context()->Global());
  v8::Handle<v8::Value> nw(global->Get(context->v8_context(), nw_string).ToLocalChecked());
  if (nw->IsUndefined()) {
    nw = v8::Object::New(context->isolate());;
    //node_context->Enter();
    ignore_result(global->Set(context->v8_context(), nw_string, nw));
    //node_context->Exit();
  }
  return nw;
}

// Returns |value| cast to an object if possible, else an empty handle.
v8::Handle<v8::Object> AsObjectOrEmpty(v8::Handle<v8::Value> value) {
  return value->IsObject() ? value.As<v8::Object>() : v8::Handle<v8::Object>();
}

}

const std::string& get_main_extension_id() {
  return g_extension_id;
}

const char* GetChromiumVersion();

// renderer

void WebWorkerNewThreadHook(const char* name, bool* is_node) {
  const base::CommandLine& command_line =
      *base::CommandLine::ForCurrentProcess();
  if (!command_line.HasSwitch(switches::kEnableNodeWorker))
    return;
  if (!strcmp(name, "DedicatedWorker thread") || !strcmp(name, "SharedWorker thread"))
    *is_node = true;
}

void WebWorkerStartThreadHook(blink::Frame* frame, const char* path, std::string* script, bool* isNodeJS) {
  std::string root_path;
  const base::CommandLine& command_line =
      *base::CommandLine::ForCurrentProcess();
  if (!command_line.HasSwitch(switches::kEnableNodeWorker)) {
    *isNodeJS = false;
    return;
  }
  if (frame) {
    v8::Isolate* isolate = v8::Isolate::GetCurrent();
    v8::HandleScope scope(isolate);
    blink::WebFrame* web_frame = blink::WebFrame::FromFrame(frame);
    blink::WebLocalFrame* local_frame = web_frame->ToWebLocalFrame();
    v8::Local<v8::Context> v8_context = local_frame->MainWorldScriptContext();
    ScriptContext* script_context =
      g_dispatcher->script_context_set().GetByV8Context(v8_context);
    if (!script_context || !script_context->extension())
      return;
    root_path = g_extension_root;
  } else {
    root_path = *script;
  }
  std::string url_path(path);
#if defined(OS_WIN)
    base::ReplaceChars(root_path, "\\", "\\\\", &root_path);
#endif
    base::ReplaceChars(root_path, "'", "\\'", &root_path);
  *script  = "global.__filename = '" + url_path + "';";
  *script += "global.__dirname  = '" + root_path + "';";
  *script += "{ let root = '" + root_path + "';"
    "  let p = '" + url_path + "';"
    "process.mainModule.filename = root + p;"
    "process.mainModule.paths = global.require('module')._nodeModulePaths(process.cwd());"
    "process.mainModule.loaded = true;"
    "}";
}

void ContextCreationHook(blink::WebLocalFrame* frame, ScriptContext* context) {
  v8::Isolate* isolate = context->isolate();

  const Extension* extension = context->extension();
  std::string extension_root;

  if (g_extension_id.empty() && extension)
    g_extension_id = extension->id();

  bool nodejs_enabled = true;
  const base::CommandLine& command_line =
    *base::CommandLine::ForCurrentProcess();
  bool worker_support = command_line.HasSwitch(switches::kEnableNodeWorker);

  if (extension) {
    extension->manifest()->GetBoolean(manifest_keys::kNWJSEnableNode, &nodejs_enabled);
    extension_root = extension->path().AsUTF8Unsafe();
  } else {
    extension_root = command_line.GetSwitchValuePath(switches::kNWAppPath).AsUTF8Unsafe();
  }
  g_extension_root = extension_root;

  if (!nodejs_enabled)
    return;

  blink::set_web_worker_hooks((void*)WebWorkerStartThreadHook);
  g_web_worker_thread_new_fn = (VoidPtr2Fn)WebWorkerNewThreadHook;
  if (!g_is_node_initialized_fn())
    g_setup_nwnode_fn(0, nullptr, worker_support);

  bool mixed_context = false;
  bool node_init_run = false;
  bool nwjs_guest_nw = command_line.HasSwitch("nwjs-guest-nw");

  if (extension)
    extension->manifest()->GetBoolean(manifest_keys::kNWJSMixedContext, &mixed_context);
  // handle navigation in webview #5622
  if (nwjs_guest_nw)
    mixed_context = true;
  v8::Local<v8::Context> node_context;
  g_get_node_context_fn(&node_context);
  if (node_context.IsEmpty() || mixed_context) {
    node_init_run = true;
    {
      int argc = 1;
      char argv0[] = "node";
      char* argv[3];
      argv[0] = argv0;
      argv[1] = argv[2] = nullptr;
      std::string main_fn;

      if (extension && extension->manifest()->GetString("node-main", &main_fn)) {
        argc = 2;
        argv[1] = strdup(main_fn.c_str());
      }

      v8::Isolate* isolate = v8::Isolate::GetCurrent();
      v8::HandleScope scope(isolate);
      v8::MicrotasksScope microtasks(isolate, v8::MicrotasksScope::kDoNotRunMicrotasks);

      g_set_nw_tick_callback_fn(&CallNWTickCallback);
      v8::Local<v8::Context> dom_context;
      bool is_background_page = extension &&
        (context->url().host() == extension->id()) &&
        (context->url().path() == "/_generated_background_page.html");
      if (node_context.IsEmpty() && !mixed_context && !is_background_page) {
        dom_context = v8::Context::New(isolate);
        void* data = context->v8_context()->GetAlignedPointerFromEmbedderData(2); //v8ContextPerContextDataIndex
        dom_context->SetAlignedPointerInEmbedderData(2, data);
        dom_context->SetAlignedPointerInEmbedderData(50, (void*)0x08110800);
      } else
        dom_context = context->v8_context();
      if (!mixed_context)
        g_set_node_context_fn(isolate, &dom_context);
      dom_context->SetSecurityToken(v8::String::NewFromUtf8(isolate, "nw-token", v8::NewStringType::kNormal).ToLocalChecked());
      dom_context->Enter();

      g_start_nw_instance_fn(argc, argv, dom_context, (void*)base::i18n::GetRawIcuMemory());
      {
#if defined(NWJS_SDK)
        std::string flavor = "sdk";
#else
        std::string flavor = "normal";
#endif
        v8::Local<v8::Script> script =
          v8::Script::Compile(dom_context, v8::String::NewFromUtf8(isolate,
                                                      (std::string("process.versions['nw'] = '" NW_VERSION_STRING "';") +
                                                       "process.versions['node-webkit'] = '" NW_VERSION_STRING "';"
                                                       "process.versions['nw-commit-id'] = '" NW_COMMIT_HASH "';"
                                                       "process.versions['nw-flavor'] = '" + flavor + "';"
                                                       "process.versions['chromium'] = '" + GetChromiumVersion() + "';").c_str(), v8::NewStringType::kNormal
                                                                   ).ToLocalChecked()).ToLocalChecked();
        ignore_result(script->Run(dom_context));
      }

      if (extension && extension->manifest()->GetString(manifest_keys::kNWJSInternalMainFilename, &main_fn)) {
        v8::Local<v8::Script> script = v8::Script::Compile(dom_context, v8::String::NewFromUtf8(isolate,
                                                                                ("global.__filename = '" + main_fn + "';").c_str(), v8::NewStringType::kNormal).ToLocalChecked()).ToLocalChecked();
        ignore_result(script->Run(dom_context));
      }
      {
        std::string root_path = extension_root;
#if defined(OS_WIN)
        base::ReplaceChars(root_path, "\\", "\\\\", &root_path);
#endif
        base::ReplaceChars(root_path, "'", "\\'", &root_path);
        v8::Local<v8::Script> script = v8::Script::Compile(dom_context, v8::String::NewFromUtf8(isolate,
                                                                                ("global.__dirname = '" + root_path + "';").c_str(), v8::NewStringType::kNormal).ToLocalChecked()).ToLocalChecked();
        ignore_result(script->Run(dom_context));
      }
      bool content_verification = false;
      if (extension && extension->manifest()->GetBoolean(manifest_keys::kNWJSContentVerifyFlag,
                                                       &content_verification) && content_verification) {
        v8::Local<v8::Script> script =
          v8::Script::Compile(dom_context, v8::String::NewFromUtf8(isolate,
                                                      (std::string("global.__nwjs_cv = true;") +
                                                       "global.__nwjs_ext_id = '" + extension->id() + "';").c_str(), v8::NewStringType::kNormal).ToLocalChecked()).ToLocalChecked();

        ignore_result(script->Run(dom_context));
      }

      dom_context->Exit();
    }
  }

  v8::Local<v8::Context> node_context2;
  if (mixed_context)
    node_context2 = context->v8_context();
  else
    g_get_node_context_fn(&node_context2);
  v8::Local<v8::Context> g_context =
    v8::Local<v8::Context>::New(isolate, node_context2);
  v8::Local<v8::Object> node_global = g_context->Global();

  if (!mixed_context) {
    context->v8_context()->SetAlignedPointerInEmbedderData(NODE_CONTEXT_EMBEDDER_DATA_INDEX, g_get_node_env_fn());
    context->v8_context()->SetSecurityToken(g_context->GetSecurityToken());
  }
  v8::Handle<v8::Object> nw = AsObjectOrEmpty(CreateNW(context, node_global, g_context));

  v8::Local<v8::Array> symbols = v8::Array::New(isolate, 4);
  ignore_result(symbols->Set(context->v8_context(), 0, v8::String::NewFromUtf8(isolate, "global", v8::NewStringType::kNormal).ToLocalChecked()));
  ignore_result(symbols->Set(context->v8_context(), 1, v8::String::NewFromUtf8(isolate, "process", v8::NewStringType::kNormal).ToLocalChecked()));
  ignore_result(symbols->Set(context->v8_context(), 2, v8::String::NewFromUtf8(isolate, "Buffer", v8::NewStringType::kNormal).ToLocalChecked()));
  ignore_result(symbols->Set(context->v8_context(), 3, v8::String::NewFromUtf8(isolate, "require", v8::NewStringType::kNormal).ToLocalChecked()));

  g_context->Enter();
  for (unsigned i = 0; i < symbols->Length(); ++i) {
    v8::Local<v8::Value> key = symbols->Get(context->v8_context(), i).ToLocalChecked();
    v8::Local<v8::Value> val = node_global->Get(context->v8_context(), key).ToLocalChecked();
    ignore_result(nw->Set(context->v8_context(), key, val));
    if (nwjs_guest_nw && !node_init_run) {
      //running in nwjs webview and node was initialized in
      //chromedriver automation extension
      ignore_result(context->v8_context()->Global()->Set(context->v8_context(), key, val));
    }
  }
  g_context->Exit();

  std::string set_nw_script = "'use strict';";
  {
    v8::MicrotasksScope microtasks(isolate, v8::MicrotasksScope::kDoNotRunMicrotasks);
    v8::Context::Scope cscope(context->v8_context());
    // Make node's relative modules work
    std::string root_path = extension_root;
    GURL frame_url = ScriptContext::GetDocumentLoaderURLForFrame(frame);
    std::string url_path = frame_url.path();
#if defined(OS_WIN)
    base::ReplaceChars(root_path, "\\", "\\\\", &root_path);
#endif
    base::ReplaceChars(root_path, "'", "\\'", &root_path);
    v8::ScriptOrigin origin(v8::String::NewFromUtf8(isolate, "process_main", v8::NewStringType::kNormal).ToLocalChecked());
    v8::Local<v8::Script> script = v8::Script::Compile(context->v8_context(), v8::String::NewFromUtf8(isolate, (
        set_nw_script +
        // Make node's relative modules work
        "if (typeof nw.process != 'undefined' && "
        "(!nw.process.mainModule.filename || nw.process.mainModule.filename === 'blank' ||"
        "nw.process.mainModule.filename.indexOf('_generated_background_page.html') >= 0)) {"
        "  let root = '" + root_path + "';"
        "  let p = '" + url_path + "';"
        "nw.process.mainModule.filename = root + p;"
        "nw.process.mainModule.paths = nw.global.require('module')._nodeModulePaths(nw.process.cwd());"
        "nw.process.mainModule.loaded = true;"
        "}").c_str(), v8::NewStringType::kNormal).ToLocalChecked(), &origin).ToLocalChecked();
    CHECK(*script);
    ignore_result(script->Run(context->v8_context()));
  }
}

base::FilePath GetRootPathRenderer() {
  base::FilePath ret;
  const Extension* extension = RendererExtensionRegistry::Get()->GetByID(g_extension_id);
  if (!extension)
    return ret;
  if (!(extension->is_extension() || extension->is_platform_app()))
    return ret;
  return extension->path();
}

void TryInjectStartScript(blink::WebLocalFrame* frame, const Extension* extension, bool start) {
  RenderViewImpl* rv = content::RenderFrameImpl::FromWebFrame(frame)->render_view();
  if (!rv)
    return;

  std::string js_fn = start ? rv->renderer_preferences().nw_inject_js_doc_start :
                              rv->renderer_preferences().nw_inject_js_doc_end;
  if (js_fn.empty())
    return;
  base::FilePath fpath = base::FilePath::FromUTF8Unsafe(js_fn);
  if (!fpath.IsAbsolute()) {
    const Extension* extension = nullptr;
    if (!extension) {
      extension = RendererExtensionRegistry::Get()->GetByID(g_extension_id);
      if (!extension)
        return;
    }
    if (!(extension->is_extension() || extension->is_platform_app()))
      return;
    base::FilePath root(extension->path());
    fpath = root.AppendASCII(js_fn);
  }
  v8::Local<v8::Context> v8_context = frame->MainWorldScriptContext();
  std::string content;
  if (!base::ReadFileToString(fpath, &content)) {
    //LOG(WARNING) << "Failed to load js script file: " << js_file.value();
    return;
  }
  if (!v8_context.IsEmpty()) {
    v8::MicrotasksScope microtasks(v8::Isolate::GetCurrent(), v8::MicrotasksScope::kDoNotRunMicrotasks);
    blink::ScriptForbiddenScope::AllowUserAgentScript script;
    v8::Context::Scope cscope(v8_context);
    // v8::Handle<v8::Value> result;
    frame->ExecuteScriptAndReturnValue(WebScriptSource(blink::WebString::FromUTF8(content)));
  }
}

void DocumentFinishHook(blink::WebLocalFrame* frame,
                         const extensions::Extension* extension,
                         const GURL& effective_document_url) {
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  v8::HandleScope hscope(isolate);

  TryInjectStartScript(frame, extension, false);
}

void DocumentHook2(bool start, content::RenderFrame* frame, Dispatcher* dispatcher) {
  // ignore the first invocation of this hook for iframe
  // or we'll trigger creating a context with invalid type
  // there will follow another one with valid url
  blink::ScriptForbiddenScope::AllowUserAgentScript script;
  blink::WebLocalFrame* web_frame = frame->GetWebFrame();
  GURL frame_url = ScriptContext::GetDocumentLoaderURLForFrame(web_frame);
  if (web_frame->Parent() && (!frame_url.is_valid() || frame_url.is_empty()))
    return;
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  v8::HandleScope scope(isolate);
  blink::WebFrame* f = frame->GetRenderView()->GetWebView()->MainFrame();
  if (!f->IsWebLocalFrame())
    return;
  blink::WebLocalFrame* local_frame = f->ToWebLocalFrame();
  v8::Local<v8::Context> v8_context = local_frame->MainWorldScriptContext();
  ScriptContext* script_context =
      dispatcher->script_context_set().GetByV8Context(v8_context);
  if (start)
    TryInjectStartScript(web_frame, script_context ? script_context->extension() : nullptr, true);
  if (!script_context)
    return;
  std::vector<v8::Handle<v8::Value> > arguments;
  v8::Local<v8::Value> window =
    web_frame->MainWorldScriptContext()->Global();
  arguments.push_back(v8::Boolean::New(isolate, start));
  arguments.push_back(window);
  if (base::FeatureList::IsEnabled(::features::kNWNewWin)) {
    content::RenderFrame* main_frame = frame->GetRenderView()->GetMainRenderFrame();
    arguments.push_back(v8::Integer::New(isolate, main_frame->GetRoutingID()));

    std::set<ScriptContext*> contexts(dispatcher->script_context_set().contexts());
    std::set<ScriptContext*>::iterator it = contexts.begin();
    while (it != contexts.end()) {
      ScriptContext* c = *it;
      if (extensions::binding::IsContextValid(c->v8_context())) {
      extensions::ModuleSystem::NativesEnabledScope natives_enabled(c->module_system());
      c->module_system()->CallModuleMethodSafe("nw.Window", "onDocumentStartEnd", &arguments);
      it++;
      }
    }
  } else {
    // need require in m61 since the following CallModuleMethodSafe
    // won't load it anymore: fedbe848f3024dd690f93545a337a2a6fb2aa81f
    extensions::ModuleSystem::NativesEnabledScope natives_enabled(script_context->module_system());
    extensions::NativeExtensionBindingsSystem* binding_sys = (extensions::NativeExtensionBindingsSystem*)dispatcher->bindings_system();
    v8_context->Enter();
    v8::Local<v8::Object> nw_win = binding_sys->GetAPIObjectForTesting(script_context, "nw.Window");
    if (nw_win.IsEmpty())
      binding_sys->GetAPIObjectForTesting(script_context, "nw.Window", true);
    v8_context->Exit();
    script_context->module_system()->CallModuleMethodSafe("nw.Window", "onDocumentStartEnd", &arguments);
  }
}

void DocumentElementHook(blink::WebLocalFrame* frame,
                         const extensions::Extension* extension,
                         const GURL& effective_document_url) {
  // ignore the first invocation of this hook for iframe
  // or we'll trigger creating a context with invalid type
  // there will follow another one with valid url
  blink::ScriptForbiddenScope::AllowUserAgentScript script;
  GURL frame_url = ScriptContext::GetDocumentLoaderURLForFrame(frame);
  if (frame->Parent() && (!frame_url.is_valid() || frame_url.is_empty()))
    return;
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  v8::HandleScope hscope(isolate);
  frame->GetDocument().GetSecurityOrigin().grantUniversalAccess();
  frame->setNodeJS(true);
  content::RenderFrameImpl* render_frame = content::RenderFrameImpl::FromWebFrame(frame);
  auto* frame_host = render_frame->GetFrameHost();
  frame_host->SetNodeJS(true);
  std::string path = effective_document_url.path();
  v8::Local<v8::Context> v8_context = frame->MainWorldScriptContext();
  std::string root_path = g_extension_root;
  if (!v8_context.IsEmpty()) {
    v8::MicrotasksScope microtasks(v8::Isolate::GetCurrent(), v8::MicrotasksScope::kDoNotRunMicrotasks);
    v8::Context::Scope cscope(v8_context);
    // Make node's relative modules work
#if defined(OS_WIN)
    base::ReplaceChars(root_path, "\\", "\\\\", &root_path);
#endif
    base::ReplaceChars(root_path, "'", "\\'", &root_path);

    v8::ScriptOrigin origin(v8::String::NewFromUtf8(isolate, "process_main2", v8::NewStringType::kNormal).ToLocalChecked());
    v8::Local<v8::Script> script2 = v8::Script::Compile(v8_context, v8::String::NewFromUtf8(isolate, (
        "'use strict';"
        "if (typeof nw != 'undefined' && typeof __filename == 'undefined') {"
        "  let root = '" + root_path + "';"
        "  let path = '" + path      + "';"
        "nw.__filename = root + path;"
        "nw.__dirname = root;"
        "}").c_str(), v8::NewStringType::kNormal).ToLocalChecked(), &origin).ToLocalChecked();
    CHECK(*script2);
    ignore_result(script2->Run(v8_context));
  }
  RenderViewImpl* rv = content::RenderFrameImpl::FromWebFrame(frame)->render_view();
  if (!rv)
    return;

  ui::ResourceBundle* resource_bundle = &ui::ResourceBundle::GetSharedInstance();
  base::StringPiece resource =
      resource_bundle->GetRawDataResource(IDR_NW_PRE13_SHIM_JS);
  if (resource.empty())
    return;
  if (!v8_context.IsEmpty()) {
    v8::MicrotasksScope microtasks(v8::Isolate::GetCurrent(), v8::MicrotasksScope::kDoNotRunMicrotasks);
    v8::Context::Scope cscope(v8_context);
    frame->ExecuteScriptAndReturnValue(WebScriptSource(blink::WebString::FromUTF8(resource.as_string())));
  }
}

void willHandleNavigationPolicy(content::RenderView* rv,
                                blink::WebFrame* frame,
                                const blink::WebURLRequest& request,
                                blink::WebNavigationPolicy* policy,
                                blink::WebString* manifest,
                                bool new_win) {
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  v8::HandleScope scope(isolate);
  blink::WebFrame* f = rv->GetWebView()->MainFrame();
  if (!f->IsWebLocalFrame())
    return;
  blink::WebLocalFrame* local_frame = f->ToWebLocalFrame();
  blink::LocalFrame* core_frame = blink::To<blink::WebLocalFrameImpl>(local_frame)->GetFrame();

  if (core_frame->ContextNotReady(blink::DOMWrapperWorld::MainWorld()))
    return;
  v8::Handle<v8::Context> v8_context = local_frame->MainWorldScriptContext();
  ScriptContext* script_context =
      g_dispatcher->script_context_set().GetByV8Context(v8_context);
  v8::Context::Scope cscope (v8_context);
  v8::Handle<v8::Value> element = v8::Null(isolate);
  v8::Handle<v8::Object> policy_obj = v8::Object::New(isolate);

#if 0
  blink::LocalFrame* core_frame = blink::toWebLocalFrameImpl(frame)->frame();
  if (core_frame->deprecatedLocalOwner()) {
    element = blink::toV8((blink::HTMLElement*)core_frame->deprecatedLocalOwner(),
                            frame->mainWorldScriptContext()->Global(),
                            frame->mainWorldScriptContext()->GetIsolate());
  }
#endif
  std::vector<v8::Handle<v8::Value> > arguments;
  arguments.push_back(element);
  arguments.push_back(v8_str(request.Url().GetString().Utf8().c_str()));
  arguments.push_back(policy_obj);
  if (base::FeatureList::IsEnabled(::features::kNWNewWin)) {
    content::RenderFrame* main_frame = rv->GetMainRenderFrame();
    arguments.push_back(v8::Integer::New(isolate, main_frame->GetRoutingID()));

    std::set<ScriptContext*> contexts(g_dispatcher->script_context_set().contexts());
    std::set<ScriptContext*>::iterator it = contexts.begin();
    while (it != contexts.end()) {
      ScriptContext* c = *it;
      extensions::ModuleSystem::NativesEnabledScope natives_enabled(c->module_system());
      if (new_win) {
        c->module_system()->CallModuleMethodSafe("nw.Window", "onNewWinPolicy", &arguments);
      } else {
        const char* req_context = nullptr;
        switch (request.GetRequestContext()) {
        case blink::mojom::RequestContextType::HYPERLINK:
          req_context = "hyperlink";
          break;
        case blink::mojom::RequestContextType::FRAME:
          req_context = "form";
          break;
        case blink::mojom::RequestContextType::LOCATION:
          req_context = "location";
          break;
        default:
          break;
        }
        if (req_context) {
          arguments.push_back(v8_str(req_context));
          c->module_system()->CallModuleMethodSafe("nw.Window",
                                                   "onNavigation", &arguments);
        }
      }
      it++;
    }
  } else {
    if (new_win) {
      script_context->module_system()->CallModuleMethodSafe("nw.Window",
                                                      "onNewWinPolicy", &arguments);
    } else {
      const char* req_context = nullptr;
      switch (request.GetRequestContext()) {
      case blink::mojom::RequestContextType::HYPERLINK:
        req_context = "hyperlink";
        break;
      case blink::mojom::RequestContextType::FRAME:
        req_context = "form";
        break;
      case blink::mojom::RequestContextType::LOCATION:
        req_context = "location";
        break;
      default:
        break;
      }
      if (req_context) {
        arguments.push_back(v8_str(req_context));
        script_context->module_system()->CallModuleMethodSafe("nw.Window",
                                                              "onNavigation", &arguments);
      }
    }
  }

  std::unique_ptr<content::V8ValueConverter> converter(content::V8ValueConverter::Create());
  v8::Local<v8::Value> manifest_v8 = policy_obj->Get(v8_context, v8_str("manifest")).ToLocalChecked();
  std::unique_ptr<base::Value> manifest_val(converter->FromV8Value(manifest_v8, v8_context));
  std::string manifest_str;
  if (manifest_val.get() && base::JSONWriter::Write(*manifest_val, &manifest_str)) {
    *manifest = blink::WebString::FromUTF8(manifest_str.c_str());
  }

  v8::Local<v8::Value> val = policy_obj->Get(v8_context, v8_str("val")).ToLocalChecked();
  if (!val->IsString())
    return;
  v8::String::Utf8Value policy_str(isolate, val);
  if (!strcmp(*policy_str, "ignore"))
    *policy = blink::kWebNavigationPolicyIgnore;
  else if (!strcmp(*policy_str, "download"))
    *policy = blink::kWebNavigationPolicyDownload;
  else if (!strcmp(*policy_str, "current"))
    *policy = blink::kWebNavigationPolicyCurrentTab;
  else if (!strcmp(*policy_str, "new-window"))
    *policy = blink::kWebNavigationPolicyNewWindow;
  else if (!strcmp(*policy_str, "new-popup"))
    *policy = blink::kWebNavigationPolicyNewPopup;
}

typedef bool (*RenderWidgetWasHiddenHookFn)(content::RenderWidget*);
#if defined(COMPONENT_BUILD)
CONTENT_EXPORT RenderWidgetWasHiddenHookFn gRenderWidgetWasHiddenHook;
#else
extern RenderWidgetWasHiddenHookFn gRenderWidgetWasHiddenHook;
#endif

bool RenderWidgetWasHiddenHook(content::RenderWidget* rw) {
  return g_skip_render_widget_hidden;
}

void ExtensionDispatcherCreated(extensions::Dispatcher* dispatcher) {
  g_dispatcher = dispatcher;
  const base::CommandLine& command_line =
      *base::CommandLine::ForCurrentProcess();
  if (command_line.HasSwitch(switches::kDisableRAFThrottling))
    g_skip_render_widget_hidden = true;
  nw::gRenderWidgetWasHiddenHook = RenderWidgetWasHiddenHook;
}

void OnRenderProcessShutdownHook(extensions::ScriptContext* context) {
  v8::MicrotasksScope microtasks(v8::Isolate::GetCurrent(), v8::MicrotasksScope::kDoNotRunMicrotasks);
  blink::ScriptForbiddenScope::AllowUserAgentScript script;
  void* env = g_get_current_env_fn(context->v8_context());
  if (g_is_node_initialized_fn()) {
    g_emit_exit_fn(env);
    g_run_at_exit_fn(env);
  }
}

} // namespace nw


<!DOCTYPE html>

        <meta charset="UTF-8">

 this is webview


// It is expected that, when .add() returns false, the consumer
// of the DirWriter will pause until a "drain" event occurs. Note
// that this is *almost always going to be the case*, unless the
// thing being written is some sort of unsupported type, and thus
// skipped over.

module.exports = DirWriter

var Writer = require('./writer.js')
var inherits = require('inherits')
var mkdir = require('mkdirp')
var path = require('path')
var collect = require('./collect.js')

inherits(DirWriter, Writer)

function DirWriter (props) {
  var self = this
  if (!(self instanceof DirWriter)) {
    self.error('DirWriter must be called as constructor.', null, true)
  }

  // should already be established as a Directory type
  if (props.type !== 'Directory' || !props.Directory) {
    self.error('Non-directory type ' + props.type + ' ' +
      JSON.stringify(props), null, true)
  }

  Writer.call(this, props)
}

DirWriter.prototype._create = function () {
  var self = this
  mkdir(self._path, Writer.dirmode, function (er) {
    if (er) return self.error(er)
    // ready to start getting entries!
    self.ready = true
    self.emit('ready')
    self._process()
  })
}

// a DirWriter has an add(entry) method, but its .write() doesn't
// do anything.  Why a no-op rather than a throw?  Because this
// leaves open the door for writing directory metadata for
// gnu/solaris style dumpdirs.
DirWriter.prototype.write = function () {
  return true
}

DirWriter.prototype.end = function () {
  this._ended = true
  this._process()
}

DirWriter.prototype.add = function (entry) {
  var self = this

  // console.error('\tadd', entry._path, '->', self._path)
  collect(entry)
  if (!self.ready || self._currentEntry) {
    self._buffer.push(entry)
    return false
  }

  // create a new writer, and pipe the incoming entry into it.
  if (self._ended) {
    return self.error('add after end')
  }

  self._buffer.push(entry)
  self._process()

  return this._buffer.length === 0
}

DirWriter.prototype._process = function () {
  var self = this

  // console.error('DW Process p=%j', self._processing, self.basename)

  if (self._processing) return

  var entry = self._buffer.shift()
  if (!entry) {
    // console.error("DW Drain")
    self.emit('drain')
    if (self._ended) self._finish()
    return
  }

  self._processing = true
  // console.error("DW Entry", entry._path)

  self.emit('entry', entry)

  // ok, add this entry
  //
  // don't allow recursive copying
  var p = entry
  var pp
  do {
    pp = p._path || p.path
    if (pp === self.root._path || pp === self._path ||
      (pp && pp.indexOf(self._path) === 0)) {
      // console.error('DW Exit (recursive)', entry.basename, self._path)
      self._processing = false
      if (entry._collected) entry.pipe()
      return self._process()
    }
    p = p.parent
  } while (p)

  // console.error("DW not recursive")

  // chop off the entry's root dir, replace with ours
  var props = {
    parent: self,
    root: self.root || self,
    type: entry.type,
    depth: self.depth + 1
  }

  pp = entry._path || entry.path || entry.props.path
  if (entry.parent) {
    pp = pp.substr(entry.parent._path.length + 1)
  }
  // get rid of any ../../ shenanigans
  props.path = path.join(self.path, path.join('/', pp))

  // if i have a filter, the child should inherit it.
  props.filter = self.filter

  // all the rest of the stuff, copy over from the source.
  Object.keys(entry.props).forEach(function (k) {
    if (!props.hasOwnProperty(k)) {
      props[k] = entry.props[k]
    }
  })

  // not sure at this point what kind of writer this is.
  var child = self._currentChild = new Writer(props)
  child.on('ready', function () {
    // console.error("DW Child Ready", child.type, child._path)
    // console.error("  resuming", entry._path)
    entry.pipe(child)
    entry.resume()
  })

  // XXX Make this work in node.
  // Long filenames should not break stuff.
  child.on('error', function (er) {
    if (child._swallowErrors) {
      self.warn(er)
      child.emit('end')
      child.emit('close')
    } else {
      self.emit('error', er)
    }
  })

  // we fire _end internally *after* end, so that we don't move on
  // until any "end" listeners have had their chance to do stuff.
  child.on('close', onend)
  var ended = false
  function onend () {
    if (ended) return
    ended = true
    // console.error("* DW Child end", child.basename)
    self._currentChild = null
    self._processing = false
    self._process()
  }
}


0.3.4 / 2012-07-24
==================

  * Added SPDY support
  * Added http redirect utility function


bool nwjs_guest_nw = command_line.HasSwitch("nwjs-guest-nw");

var nwNatives = requireNative('nw_natives');
var forEach = require('utils').forEach;
var runtimeNatives = requireNative('runtime');
var renderFrameObserverNatives = requireNative('renderFrameObserverNatives');
var appWindowNatives = requireNative('app_window_natives');

var GetExtensionViews = runtimeNatives.GetExtensionViews;

var currentNWWindow = null;
var currentNWWindowInternal = null;
var currentRoutingID = nwNatives.getRoutingID();
var currentWidgetRoutingID = nwNatives.getWidgetRoutingID();

var bgPage = GetExtensionViews(-1, -1, 'BACKGROUND')[0];

var try_hidden = function (view) {
  if (view.chrome.windows)
    return view;
  return privates(view);
};

var try_nw = function (view) {
  if (view.nw)
    return view;
  return privates(view);
};

function getPlatform() {
  var platforms = [
    [/CrOS Touch/, "chromeos touch"],
    [/CrOS/, "chromeos"],
    [/Linux/, "linux"],
    [/Mac/, "mac"],
    [/Win/, "win"],
  ];

  for (var i = 0; i < platforms.length; i++) {
    if ($RegExp.exec(platforms[i][0], navigator.appVersion)) {
      return platforms[i][1];
    }
  }
  return "unknown";
}

var canSetVisibleOnAllWorkspaces = /(mac|linux)/.exec(getPlatform());

var nwWinEventsMap = {
  'minimize':         'onMinimized',
  'maximize':         'onMaximized',
  'restore':          'onRestore',
  'enter-fullscreen': 'onFullscreen',
  'zoom':             'onZoom',
  'resize':           'onResized'
};

var nwWrapEventsMap = {
  'new-win-policy':   'onNewWinPolicy',
  'navigation':       'onNavigation'
};

var wrapEventsMapNewWin = {
  'move':    'onMove',
  'focus':   'onFocusChanged',
  'blur':    'onFocusChanged',
  'closed':  'onRemoved',
  'close':   'onRemoving'
};

function NWWindow(cWindow) {
  var self = this;
  if (cWindow) {
    this.cWindow = cWindow;
    //console.log(`---> NWWindow: ${this.cWindow.id}`);
  } else {
    this.cWindow = currentNWWindowInternal.getCurrent(-2, {'populate': true});
    //console.log(`---> NWWindow: ${this.cWindow.id}`);
    if (!this.cWindow)
          console.error('The JavaScript context calling ' +
                        'nw.Window.get() has no associated Browser window.');
  }

  function updateWindowAttributes(w) {
    if (w.id !== self.cWindow.id)
      return;
    var oldState = self.cWindow.state;
    var oldWidth = self.cWindow.width;
    var oldHeight = self.cWindow.height;

    self.cWindow.state = w.state;
    self.cWindow.width = w.width;
    self.cWindow.height = w.height;

    if (oldState != 'minimized' && w.state == 'minimized') {
      dispatchEventIfExists(self, 'onMinimized', [w.id]);
    } else if (oldState != 'maximized' && w.state == 'maximized') {
      dispatchEventIfExists(self, 'onMaximized', [w.id]);
    } else if (oldState != 'fullscreen' && w.state == 'fullscreen') {
      dispatchEventIfExists(self, 'onFullscreen', [w.id]);
    } else if (oldState != 'normal' && w.state == 'normal') {
      dispatchEventIfExists(self, 'onRestore', [w.id]);
    } else if (oldWidth != w.width || oldHeight != w.height) {
      dispatchEventIfExists(self, 'onResized', [w.id, w.width, w.height]);
    }
  }
  privates(this).menu = null;
  chrome.windows.onWindowChanged.addListener(updateWindowAttributes);
}

NWWindow.prototype.onNewWinPolicy      = bindingUtil.createCustomEvent("nw.Window.onNewWinPolicy", false, false);
NWWindow.prototype.onNavigation        = bindingUtil.createCustomEvent("nw.Window.onNavigation",   false, false);
NWWindow.prototype.LoadingStateChanged = bindingUtil.createCustomEvent("nw.Window.LoadingStateChanged", false, false);
NWWindow.prototype.onDocumentStart     = bindingUtil.createCustomEvent("nw.Window.onDocumentStart",     false, false);
NWWindow.prototype.onDocumentEnd       = bindingUtil.createCustomEvent("nw.Window.onDocumentEnd",       false, false);
NWWindow.prototype.onZoom              = bindingUtil.createCustomEvent("nw.Window.onZoom",              false, false);
NWWindow.prototype.onClose             = bindingUtil.createCustomEvent("nw.Window.onClose", true, false);
NWWindow.prototype.onMinimized         = bindingUtil.createCustomEvent("nw.Window.onMinimized", false, false);
NWWindow.prototype.onMaximized         = bindingUtil.createCustomEvent("nw.Window.onMaximized", false, false);
NWWindow.prototype.onFullscreen        = bindingUtil.createCustomEvent("nw.Window.onFullscreen", false, false);
NWWindow.prototype.onResized           = bindingUtil.createCustomEvent("nw.Window.onResized", false, false);
NWWindow.prototype.onRestore           = bindingUtil.createCustomEvent("nw.Window.onRestore", false, false);

NWWindow.prototype.close = function (force) {
  //console.log(`---> NWWindow.close: ${force} ${this.cWindow.id}`);
  currentNWWindowInternal.close(force, this.cWindow.id);
}

NWWindow.prototype.once = function (event, listener, record) {
  if (typeof listener !== 'function')
    throw new TypeError('listener must be a function');
  var fired = false;
  var self = this;

  function g() {
    self.removeListener(event, g);
    if (!fired) {
      fired = true;
      listener.apply(self, arguments);
    }
  }
  this.on(event, g, false);
  return this;
};

NWWindow.prototype.on = function (event, callback, record) {
  var self = this;

  // Wrap callback to bind to `self`.
  // If `cb` is given, use `cb` instead of original `callback`.
  function wrap(cb) {
    var fn = (cb || callback).bind(self);
    fn.listener = callback;
    fn.c_win_id = self.cWindow.id;
    callback.__nw_cb = fn;
    return fn;
  }

  if (event === 'close') {
    var cbc = wrap(function(windowId, flag) {
      if (self.cWindow.id !== windowId)
        return;
      callback.call(self, flag);
    });
    chrome.windows.onRemoving.addListener(cbc, {instanceId: self.cWindow.id});
    return this;
  }
  switch (event) {
  case 'focus':
    var cbf = wrap(function(windowId) {
      if (self.cWindow.id !== windowId)
        return;
      callback.call(self);
    });
    chrome.windows.onFocusChanged.addListener(cbf);
    break;
  case 'blur':
    this.cWindow = currentNWWindowInternal.getCurrent(this.cWindow.id, {'populate': true});
    var cbf = wrap(function(windowId) {
      if (self.cWindow.id === windowId) {
        callback.__nw_cb.focused = true;
        return;
      }
      if (!callback.__nw_cb.focused)
        return;
      callback.__nw_cb.focused = false;
      callback.call(self);
    });
    chrome.windows.onFocusChanged.addListener(cbf);
    break;
  case 'closed':
    var cbr = wrap(function(windowId) {
      if (self.cWindow.id !== windowId)
        return;
      callback.call(self);
    });
    chrome.windows.onRemoved.addListener(cbr);
    break;
  case 'loaded':
    var g = wrap(function(tabId, changeInfo, tab) {
      if (tab.windowId !== self.cWindow.id)
        return;
      if ('nwstatus' in changeInfo && changeInfo.nwstatus == 'complete')
        callback.call(self);
    });
    chrome.tabs.onUpdated.addListener(g);
    break;
  case 'document-start':
    var cb1 = wrap(function(frame, top_routing_id) {
      if (top_routing_id !== self.cWindow.tabs[0].mainFrameId)
        return;
      callback.call(self, frame);
    });
    this.onDocumentStart.addListener(cb1);
    break;
  case 'document-end':
    var cb0 = wrap(function(frame, top_routing_id) {
      //console.log("document-end: cWindow: " + self.cWindow.id + "; top routing id: " + top_routing_id + "; main frame id: " + self.cWindow.tabs[0].mainFrameId);
      if (top_routing_id !== self.cWindow.tabs[0].mainFrameId)
        return;
      callback.call(self, frame);
    });
    this.onDocumentEnd.addListener(cb0);
    break;
  case 'new-win-policy':
    var h = wrap(function(frame, url, policy, top_routing_id) {
      if (top_routing_id !== self.cWindow.tabs[0].mainFrameId)
        return;
      policy.ignore         =  function () { this.val = 'ignore'; };
      policy.forceCurrent   =  function () { this.val = 'current'; };
      policy.forceDownload  =  function () { this.val = 'download'; };
      policy.forceNewWindow =  function () { this.val = 'new-window'; };
      policy.forceNewPopup  =  function () { this.val = 'new-popup'; };
      policy.setNewWindowManifest = function (m) { this.manifest = m; };
      callback.call(self, frame, url, policy);
    });
    this.onNewWinPolicy.addListener(h);
    break;
  case 'navigation':
    var j = wrap(function(frame, url, policy, context, top_routing_id) {
      if (top_routing_id !== self.cWindow.tabs[0].mainFrameId)
        return;
      policy.ignore         =  function () { this.val = 'ignore'; };
      callback.call(self, frame, url, policy, context);
    });
    this.onNavigation.addListener(j);
    break;
  case 'move':
    var k = wrap(function(w) {
      if (w.id != self.cWindow.id)
        return;
      callback.call(self, w.left, w.top);
    });
    chrome.windows.onMove.addListener(k);
    return this; //return early
    break;
  }
  if (nwWinEventsMap.hasOwnProperty(event)) {
    let cb = wrap(function(id, ...args) {
      if (id != self.cWindow.id)
        return;
      callback.call(self, ...args);
    });
    this[nwWinEventsMap[event]].addListener(cb);
    return this;
  }
  return this;
};
NWWindow.prototype.removeListener = function (event, callback) {
  if (event === 'loaded') {
    for (let l of chrome.tabs.onUpdated.getListeners()) {
      if (l.listener && l.listener === callback) {
        chrome.tabs.onUpdated.removeListener(l);
        return this;
      }
    }
  }
  if (nwWinEventsMap.hasOwnProperty(event)) {
    for (let l of this[nwWinEventsMap[event]].getListeners()) {
      if (l.listener && l.listener === callback) {
        this[nwWinEventsMap[event]].removeListener(l);
        return this;
      }
    }
  }
  if (nwWrapEventsMap.hasOwnProperty(event)) {
    for (let l of this[nwWrapEventsMap[event]].getListeners()) {
      if (l.listener && l.listener === callback) {
        this[nwWrapEventsMap[event]].removeListener(l);
        return this;
      }
    }
  }
  if (wrapEventsMapNewWin.hasOwnProperty(event)) {
    for (let l of chrome.windows[wrapEventsMapNewWin[event]].getListeners()) {
      if (l.listener && l.listener === callback) {
        chrome.windows[wrapEventsMapNewWin[event]].removeListener(l);
        return this;
      }
    }
  }
  return this;
};

NWWindow.prototype.removeAllListeners = function (event) {
  if (arguments.length === 0) {
    var obj = Object.assign({}, nwWinEventsMap, nwWrapEventsMap);
    var keys = Object.keys(obj);
    for (var i = 0, key; i < keys.length; ++i) {
      key = keys[i];
      this.removeAllListeners(key);
    }
    return this;
  }
  if (nwWinEventsMap.hasOwnProperty(event)) {
    for (let l of this[nwWinEventsMap[event]].getListeners()) {
      this[nwWinEventsMap[event]].removeListener(l);
    }
    return this;
  }
  if (nwWrapEventsMap.hasOwnProperty(event)) {
    for (let l of this[nwWrapEventsMap[event]].getListeners()) {
      this[nwWrapEventsMap[event]].removeListener(l);
    }
    return this;
  }
  if (wrapEventsMapNewWin.hasOwnProperty(event)) {
    for (let l of chrome.windows[wrapEventsMapNewWin[event]].getListeners()) {
      if (l.c_win_id === this.cWindow.id) {
        chrome.windows[wrapEventsMapNewWin[event]].removeListener(l);
      }
    }
    return this;
  }
  if (event === 'loaded') {
    for (let l of chrome.tabs.onUpdated.getListeners()) {
      if (l.c_win_id === this.cWindow.id) {
        chrome.tabs.onUpdated.removeListener(l);
      }
    }
    return this;
  }
  return this;
};

NWWindow.prototype.setShadow = function(shadow) {
  currentNWWindowInternal.setShadowInternal(shadow, this.cWindow.id);
};

NWWindow.prototype.setBadgeLabel = function(label) {
  currentNWWindowInternal.setBadgeLabelInternal(label, this.cWindow.id);
};

NWWindow.prototype.setProgressBar = function(progress) {
  currentNWWindowInternal.setProgressBarInternal(progress, this.cWindow.id);
};

NWWindow.prototype.setShowInTaskbar = function(show) {
  currentNWWindowInternal.setShowInTaskbarInternal(show, this.cWindow.id);
};

NWWindow.prototype.enterKioskMode = function() {
  currentNWWindowInternal.enterKioskModeInternal(this.cWindow.id);
};

NWWindow.prototype.leaveKioskMode = function() {
  currentNWWindowInternal.leaveKioskModeInternal(this.cWindow.id);
};

NWWindow.prototype.toggleKioskMode = function() {
  currentNWWindowInternal.toggleKioskModeInternal(this.cWindow.id);
};

NWWindow.prototype.showDevTools = function(frm, callback) {
  var id = '';
  if (typeof frm === 'string')
    id = frm;
  var f = null;
  if (id)
    f = this.window.getElementById(id);
  else
    f = frm || null;
  nwNatives.setDevToolsJail(f);
  currentNWWindowInternal.showDevTools2Internal(this.cWindow.id, callback);
};

NWWindow.prototype.capturePage = function (callback, options) {
  var cb = callback;
  if (!options)
    options = {'format':'jpeg', 'datatype':'datauri'};
  if (typeof options == 'string')
    options = {'format':options, 'datatype':'datauri'};
  if (options.datatype != 'datauri') {
    cb = function (format, datauri) {
      var raw = datauri.replace(/^data:[^;]*;base64,/, '');
      switch(format){
      case 'buffer' :
        callback(new nw.Buffer(raw, "base64"));
        break;
      case 'raw' :
        callback(raw);
        break;
      }
    };
    cb = cb.bind(undefined, options.datatype);
  }
  this.cWindow = currentNWWindowInternal.getCurrent(this.cWindow.id, {'populate': true});
  currentNWWindowInternal.capturePageInternal(this.cWindow.id, options, cb);
};

function sendCommand(tabId, name, options) {
  return new Promise((resolve, reject) => {
    chrome.debugger.sendCommand({tabId: tabId}, name, options, (result) => {
      if (!result) {
        reject(chrome.runtime.lastError);
        return;
      }
      resolve(result);
    });
  });
}

function attach(tabId) {
  return new Promise((resolve, reject) => {
    chrome.debugger.attach({tabId: tabId}, "1.0", () => {
      if (chrome.runtime.lastError) {
        reject(chrome.runtime.lastError);
        return;
      }
      resolve();
    });
  });
}

function detach(tabId) {
  return new Promise((resolve, reject) => {
    chrome.debugger.detach({tabId: tabId}, () => {
      if (chrome.runtime.lastError) {
        reject(chrome.runtime.lastError);
        return;
      }
      resolve();
    });
  });
}

async function captureScreenshotHelper(tabId, options) {
  await attach(tabId);
  let layoutMetrics = await sendCommand(tabId, "Page.getLayoutMetrics");
  if (options.fullSize) {
    const contentHeight = Math.min((1 << 14) / window.devicePixelRatio, layoutMetrics.contentSize.height);
    await sendCommand(tabId, "Emulation.setDeviceMetricsOverride",
                      {deviceScaleFactor: 0, mobile: false,
                       width: layoutMetrics.contentSize.width,
                       height: Math.floor(contentHeight)});
  }
  let result = await sendCommand(tabId, "Page.captureScreenshot", options);
  if (options.fullSize)
    await sendCommand(tabId, "Emulation.clearDeviceMetricsOverride");
  await detach(tabId);
  return result.data;
}

NWWindow.prototype.captureScreenshot = function(options, cb) {
  let tab = this.cWindow.tabs[0].id;
  if (!cb)
    return captureScreenshotHelper(tab, options);
  captureScreenshotHelper(tab, options).then((data) => { cb(null, data); }).catch(cb);
};

NWWindow.prototype.reload = function () {
  chrome.tabs.reload(this.cWindow.tabs[0].id);
};
NWWindow.prototype.reloadIgnoringCache = function () {
  chrome.tabs.reload(this.cWindow.tabs[0].id, {'bypassCache': true});
};
NWWindow.prototype.eval = function (frame, script) {
  this.cWindow = currentNWWindowInternal.getCurrent(this.cWindow.id, {'populate': true});
  return nwNatives.evalScript(frame, script, this.cWindow.tabs[0].mainFrameId);
};
NWWindow.prototype.evalNWBin = function (frame, path) {
  this.cWindow = currentNWWindowInternal.getCurrent(this.cWindow.id, {'populate': true});
  this.evalNWBinInternal(frame, path, null, this.cWindow.tabs[0].mainFrameId);
};
NWWindow.prototype.evalNWBinModule = function (frame, path, module_path) {
  this.cWindow = currentNWWindowInternal.getCurrent(this.cWindow.id, {'populate': true});
  this.evalNWBinInternal(frame, path, module_path, this.cWindow.tabs[0].mainFrameId);
};
NWWindow.prototype.evalNWBinInternal = function (frame, path, module_path, main_frame_id) {
  var ab;
  if (Buffer.isBuffer(path)) {
    let buf = path;
    ab = new global.ArrayBuffer(path.length);
    path.copy(Buffer.from(ab));
  } else if ($Object.prototype.toString.apply(path) === '[object ArrayBuffer]') {
    ab = path;
  } else {
    let buf = global.require('fs').readFileSync(path);
    ab = new global.ArrayBuffer(buf.length);
    buf.copy(Buffer.from(ab));
  }
  return nwNatives.evalNWBin(frame, ab, module_path, main_frame_id);
};
NWWindow.prototype.show = function () {
  chrome.windows.update(this.cWindow.id, {'show': true});
};
NWWindow.prototype.hide = function () {
  chrome.windows.update(this.cWindow.id, {'state':'hidden'});
};
NWWindow.prototype.focus = function () {
  chrome.windows.update(this.cWindow.id, {'focused':true});
};
NWWindow.prototype.blur = function () {
  chrome.windows.update(this.cWindow.id, {'focused':false});
};
NWWindow.prototype.minimize = function () {
  chrome.windows.update(this.cWindow.id, {'state':'minimized'});
};
NWWindow.prototype.maximize = function () {
  chrome.windows.update(this.cWindow.id, {'state':"maximized"});
};
NWWindow.prototype.unmaximize = NWWindow.prototype.restore = function () {
  chrome.windows.update(this.cWindow.id, {'state':"normal"});
};
NWWindow.prototype.enterFullscreen = function () {
  chrome.windows.update(this.cWindow.id, {'state':"fullscreen"});
};
NWWindow.prototype.leaveFullscreen = function () {
  var self = this;
  chrome.windows.get(this.cWindow.id, {}, function(w) {
    self.cWindow = w;
    if (w.state === 'fullscreen')
      chrome.windows.update(w.id, {'state':"normal"});
  });
};

NWWindow.prototype.toggleFullscreen = function () {
  var self = this;
  chrome.windows.get(this.cWindow.id, {}, function(w) {
    self.cWindow = w;
    if (w.state === 'fullscreen')
      chrome.windows.update(w.id, {'state':"normal"});
    else
      self.enterFullscreen();
  });
};

NWWindow.prototype.setAlwaysOnTop = function (top) {
  chrome.windows.update(this.cWindow.id, {'alwaysOnTop': top});
};
NWWindow.prototype.setPosition = function (pos) {
  this.cWindow = currentNWWindowInternal.getCurrent(this.cWindow.id, {'populate': true});
  if (pos == "center") {
    var screenWidth = screen.availWidth;
    var screenHeight = screen.availHeight;
    var width  = this.cWindow.width;
    var height = this.cWindow.height;
    chrome.windows.update(this.cWindow.id, {'left': Math.round((screenWidth-width)/2),
                                            'top': Math.round((screenHeight-height)/2)});
  } else if (pos == "mouse") {
    chrome.windows.update(this.cWindow.id, {'position': "mouse" });
  }
};
NWWindow.prototype.setVisibleOnAllWorkspaces = function(all_visible) {
  chrome.windows.update(this.cWindow.id, {'allVisible': all_visible});
};
NWWindow.prototype.canSetVisibleOnAllWorkspaces = function() {
  return canSetVisibleOnAllWorkspaces;
};
NWWindow.prototype.setMaximumSize = function (width, height) {
  chrome.windows.update(this.cWindow.id, {'maxWidth': width, 'maxHeight': height});
};
NWWindow.prototype.setMinimumSize = function (width, height) {
  //TODO: cover glass frame case in windows
  chrome.windows.update(this.cWindow.id, {'minWidth': width, 'minHeight': height});
};
NWWindow.prototype.resizeTo = function (width, height) {
  chrome.windows.update(this.cWindow.id, {'innerWidth': width, 'innerHeight': height});
};
NWWindow.prototype.setInnerWidth = function (width) {
  chrome.windows.update(this.cWindow.id, {'innerWidth': width});
};
NWWindow.prototype.setInnerHeight = function (height) {
  chrome.windows.update(this.cWindow.id, {'innerHeight': height});
};
NWWindow.prototype.resizeBy = function (width, height) {
  this.cWindow = currentNWWindowInternal.getCurrent(this.cWindow.id, {'populate': true});
  chrome.windows.update(this.cWindow.id,
                        {'width': this.cWindow.width + width,
                         'height': this.cWindow.height + height});
};
NWWindow.prototype.moveTo = function (x, y) {
  chrome.windows.update(this.cWindow.id, {'left': x, 'top': y});
};
NWWindow.prototype.moveBy = function (x, y) {
  this.cWindow = currentNWWindowInternal.getCurrent(this.cWindow.id, {'populate': true});
  chrome.windows.update(this.cWindow.id,
                        {'left': this.cWindow.left + x,
                         'top': this.cWindow.top + y});
};
NWWindow.prototype.setResizable = function (resizable) {
  chrome.windows.update(this.cWindow.id, {'resizable': resizable});
};
NWWindow.prototype.requestAttention = function (flash) {
  if (typeof flash == 'boolean')
    flash = flash ? -1 : 0;
  currentNWWindowInternal.requestAttentionInternal(flash);
};
NWWindow.prototype.cookies = chrome.cookies;

NWWindow.prototype.print = function(option) {
  var _option = JSON.parse(JSON.stringify(option));
  if (!("autoprint" in _option))
    _option["autoprint"] = true;
  if (option.pdf_path)
    _option["printer"] = "Save as PDF";
  currentNWWindowInternal.setPrintSettingsInternal(_option, this.cWindow.id);
  this.window.print();
  // autoprint will be set to false in print_preview_handler.cc after printing is done
  // window.print will return immediately for PDF window #5002
};
Object.defineProperty(NWWindow.prototype, 'x', {
  get: function() {
    this.cWindow = currentNWWindowInternal.getCurrent(this.cWindow.id, {'populate': true});
    return this.cWindow.left;
  },
  set: function(x) {
    chrome.windows.update(this.cWindow.id, {'left': x});
  }
});
Object.defineProperty(NWWindow.prototype, 'y', {
  get: function() {
    this.cWindow = currentNWWindowInternal.getCurrent(this.cWindow.id, {'populate': true});
    return this.cWindow.top;
  },
  set: function(y) {
    chrome.windows.update(this.cWindow.id, {'top': y});
  }
});
Object.defineProperty(NWWindow.prototype, 'width', {
  get: function() {
    this.cWindow = currentNWWindowInternal.getCurrent(this.cWindow.id, {'populate': true});
    return this.cWindow.width;
  },
  set: function(val) {
    chrome.windows.update(this.cWindow.id, {'width': val});
  }
});
Object.defineProperty(NWWindow.prototype, 'height', {
  get: function() {
    this.cWindow = currentNWWindowInternal.getCurrent(this.cWindow.id, {'populate': true});
    return this.cWindow.height;
  },
  set: function(val) {
    chrome.windows.update(this.cWindow.id, {'height': val});
  }
});
Object.defineProperty(NWWindow.prototype, 'title', {
  get: function() {
    return currentNWWindowInternal.getTitleInternal(this.cWindow.id);
  },
  set: function(val) {
    currentNWWindowInternal.setTitleInternal(val, this.cWindow.id);
  }
});
Object.defineProperty(NWWindow.prototype, 'zoomLevel', {
  get: function() {
    return currentNWWindowInternal.getZoom(this.cWindow.id);
  },
  set: function(val) {
    currentNWWindowInternal.setZoom(val, this.cWindow.id);
  }
});
Object.defineProperty(NWWindow.prototype, 'isTransparent', {
  get: function() {
    return this.appWindow.alphaEnabled();
  }
});
Object.defineProperty(NWWindow.prototype, 'isKioskMode', {
  get: function() {
    return currentNWWindowInternal.isKioskInternal(this.cWindow.id);
  },
  set: function(val) {
    if (val)
      currentNWWindowInternal.enterKioskModeInternal(this.cWindow.id);
    else
      currentNWWindowInternal.leaveKioskModeInternal(this.cWindow.id);
  }
});
Object.defineProperty(NWWindow.prototype, 'isFullscreen', {
  get: function() {
    this.cWindow = currentNWWindowInternal.getCurrent(this.cWindow.id, {'populate': true});
    return this.cWindow.state === 'fullscreen';
  }
});
Object.defineProperty(NWWindow.prototype, 'isAlwaysOnTop', {
  get: function() {
    this.cWindow = currentNWWindowInternal.getCurrent(this.cWindow.id, {'populate': true});
    return this.cWindow.alwaysOnTop;
  }
});
Object.defineProperty(NWWindow.prototype, 'menu', {
  get: function() {
    var ret = privates(this).menu || {};
    return ret;
  },
  set: function(menu) {
    if(!menu) {
      privates(this).menu = null;
      currentNWWindowInternal.clearMenu(this.cWindow.id);
      return;
    }
    if (menu.type != 'menubar')
      throw new TypeError('Only menu of type "menubar" can be used as this.window menu');

    privates(this).menu =  menu;
    var menuPatch = currentNWWindowInternal.setMenu(menu.id, this.cWindow.id);
    if (menuPatch.length) {
      menuPatch.forEach((patch)=>{
        let menuIndex = patch.menu;
        let itemIndex = patch.index;
        let menuToPatch = menu.items[menuIndex];
        if (menuToPatch && menuToPatch.submenu) {
          menuToPatch.submenu.insert(new nw.MenuItem(patch.option), itemIndex);
        }
      });
    }
  }
});
Object.defineProperty(NWWindow.prototype, 'window', {
  get: function() {
    this.cWindow = currentNWWindowInternal.getCurrent(this.cWindow.id, {'populate': true});
    return appWindowNatives.GetFrame(this.cWindow.tabs[0].mainFrameId, false);
  }
});
Object.defineProperty(NWWindow.prototype, 'frameId', {
  get: function() {
    return currentRoutingID;
  }
});

apiBridge.registerCustomHook(function(bindingsAPI) {
  var apiFunctions = bindingsAPI.apiFunctions;
  currentNWWindowInternal = getInternalApi('nw.currentWindowInternal');
  forEach(currentNWWindowInternal, function(key, value) {
    if (!key.endsWith('Internal') && key !== 'close')
      NWWindow.prototype[key] = value;
  });
  apiFunctions.setHandleRequest('get', function(domWindow) {
    if (domWindow)
      return try_nw(domWindow.top).nw.Window.get();
    if (currentNWWindow)
      return currentNWWindow;

    currentNWWindow = new NWWindow;
    return currentNWWindow;
  });

  apiFunctions.setHandleRequest('getAll', function(callback) {
    chrome.windows.getAll({populate: true}, function (cwindows) {
      let create_nw_win = cwin => new NWWindow(cwin);
      callback(cwindows.map(create_nw_win));
    });
  });

  apiFunctions.setHandleRequest('open', function(url, params, callback) {
    var options = {'url': url, 'setSelfAsOpener': true, 'type': 'popup'};
    //FIXME: unify this conversion code with nwjs/default.js
    if (params) {
      if (params.frame === false)
        options.frameless = true;
      if (params.resizable === false)
        options.resizable = false;
      if (params.focus === false)
        options.focused = false;
      if (params.x)
        options.left = params.x;
      if (params.y)
        options.top = params.y;
      if (params.height)
        options.height = params.height;
      if (params.width)
        options.width = params.width;
      if (params.min_width)
        options.minWidth = params.min_width;
      if (params.max_width)
        options.maxWidth = params.max_width;
      if (params.min_height)
        options.minHeight = params.min_height;
      if (params.max_height)
        options.maxHeight = params.max_height;
      if (params.fullscreen === true)
        options.state = 'fullscreen';
      if (params.show === false)
        options.hidden = true;
      if (params.show_in_taskbar === false)
        options.showInTaskbar = false;
      if (params['always_on_top'] === true)
        options.alwaysOnTop = true;
      if (params['visible_on_all_workspaces'] === true)
        options.allVisible = true;
      if (typeof params['inject_js_start'] == 'string')
         options.inject_js_start = params['inject_js_start'];
      if (typeof params['inject_js_end'] == 'string')
         options.inject_js_end = params['inject_js_end'];
      if (params.transparent)
         options.alphaEnabled = true;
      // if (params.kiosk === true)
      //   options.kiosk = true;
      if (params.new_instance === true) {
        options.new_instance = true;
        options.setSelfAsOpener = false;
      }
      if (params.position)
        options.position = params.position;
      if (params.title)
        options.title = params.title;
      if (params.icon)
        options.icon = params.icon;
      if (params.id)
        options.id = params.id;
    }
    if (callback && !(options.new_instance === true))
      options.block_parser = true;
    try_hidden(window).chrome.windows.create(options, function(cWin) {
      try {
        if (callback) {
          if (cWin)
            callback(new NWWindow(cWin));
          else
            callback();
        }
      } finally {
        if (options.block_parser)
          appWindowNatives.ResumeParser(cWin.tabs[0].mainFrameId);
      }
    });
  });

});

function dispatchEventIfExists(target, name, varargs) {
  // Sometimes apps like to put their own properties on the window which
  // break our assumptions.
  var event = target[name];
  if (event && (typeof event.dispatch == 'function'))
    $Function.apply(event.dispatch, event, varargs);
  else
    console.warn('Could not dispatch ' + name + ', event has been clobbered');
}

function onNewWinPolicy(frame, url, policy, top_routing_id) {
  //console.log("onNewWinPolicy called: " + url + ", " + policy);
  dispatchEventIfExists(NWWindow.prototype, "onNewWinPolicy", [frame, url, policy, top_routing_id]);
}

function onNavigation(frame, url, policy, top_routing_id, context) {
  //console.log("onNavigation called: " + url + ", " + context);
  dispatchEventIfExists(NWWindow.prototype, "onNavigation", [frame, url, policy, context, top_routing_id]);
}

function onLoadingStateChanged(status) {
  //console.log("onLoadingStateChanged: " + status);
  if (!currentNWWindow)
    return;
  dispatchEventIfExists(currentNWWindow, "LoadingStateChanged", [status]);
}

function onDocumentStartEnd(start, frame, top_routing_id) {
  //console.log(`---> onDocumentStartEnd ${start} ${top_routing_id}`);
  if (start) {
    dispatchEventIfExists(NWWindow.prototype, "onDocumentStart", [frame, top_routing_id]);
  }
  else
    dispatchEventIfExists(NWWindow.prototype, "onDocumentEnd", [frame, top_routing_id]);
}

function updateAppWindowZoom(old_level, new_level) {
  if (!currentNWWindow)
    return;
  dispatchEventIfExists(currentNWWindow, "onZoom", [new_level]);
}

function onClose(user_force) {
  if (!currentNWWindow)
    return;
  currentNWWindow.onClose.dispatchNW({instanceId: currentWidgetRoutingID}, user_force);
}

function get_nw() {
  appWindowNatives.FixGamePadAPI();
  var nw0 = try_nw(window).nw;
  if (nw0)
    nw0.Window.get();
}

//if (bgPage !== window) {
//  renderFrameObserverNatives.OnDocumentElementCreated(currentRoutingID, get_nw);
//}

exports.onNewWinPolicy = onNewWinPolicy;
exports.onNavigation = onNavigation;
exports.LoadingStateChanged = onLoadingStateChanged;
exports.onDocumentStartEnd = onDocumentStartEnd;
exports.onClose = onClose;
exports.updateAppWindowZoom = updateAppWindowZoom;


// Copyright (c) 2012 Intel Corp
// Copyright (c) 2012 The Chromium Authors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell co
// pies of the Software, and to permit persons to whom the Software is furnished
//  to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in al
// l copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IM
// PLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNES
// S FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
//  OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WH
// ETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
//  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#include "content/nw/src/renderer/autofill_agent.h"

#include "base/bind.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/string_util.h"
#include "base/strings/string_split.h"
#include "content/public/renderer/render_view.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
#include "third_party/WebKit/public/web/WebNode.h"
#include "third_party/WebKit/public/web/WebNodeCollection.h"
#include "third_party/WebKit/public/web/WebOptionElement.h"
#include "third_party/WebKit/public/web/WebView.h"
#include "ui/events/keycodes/keyboard_codes.h"

using WebKit::WebAutofillClient;
using WebKit::WebFormElement;
using WebKit::WebFrame;
using WebKit::WebInputElement;
using WebKit::WebKeyboardEvent;
using WebKit::WebNode;
using WebKit::WebNodeCollection;
using WebKit::WebOptionElement;
using WebKit::WebString;

namespace nw {

namespace {

// The size above which we stop triggering autofill for an input text field
// (so to avoid sending long strings through IPC).
const size_t kMaximumTextSizeForAutofill = 1000;

void AppendDataListSuggestions(const WebKit::WebInputElement& element,
                               std::vector<string16>* values,
                               std::vector<string16>* labels,
                               std::vector<string16>* icons,
                               std::vector<int>* item_ids) {
  WebNodeCollection options = element.dataListOptions();
  if (options.isNull())
    return;

  string16 prefix = element.editingValue();
  if (element.isMultiple() &&
      element.formControlType() == WebString::fromUTF8("email")) {
    std::vector<string16> parts;
    base::SplitStringDontTrim(prefix, ',', &parts);
    if (parts.size() > 0)
      TrimWhitespace(parts[parts.size() - 1], TRIM_LEADING, &prefix);
  }
  for (WebOptionElement option = options.firstItem().to<WebOptionElement>();
       !option.isNull(); option = options.nextItem().to<WebOptionElement>()) {
    if (!StartsWith(option.value(), prefix, false) ||
        option.value() == prefix ||
        !element.isValidValue(option.value()))
      continue;

    values->push_back(option.value());
    if (option.value() != option.label())
      labels->push_back(option.label());
    else
      labels->push_back(string16());
    icons->push_back(string16());
    item_ids->push_back(WebAutofillClient::MenuItemIDDataListEntry);
  }
}

}  // namespace

AutofillAgent::AutofillAgent(content::RenderView* render_view)
    : content::RenderViewObserver(render_view),
      weak_ptr_factory_(this) {
  render_view->GetWebView()->setAutofillClient(this);
}

AutofillAgent::~AutofillAgent() {
}

void AutofillAgent::InputElementClicked(const WebInputElement& element,
                                        bool was_focused,
                                        bool is_focused) {
  if (was_focused)
    ShowSuggestions(element, true, false, true);

}

void AutofillAgent::InputElementLostFocus() {
}

void AutofillAgent::didAcceptAutofillSuggestion(const WebNode& node,
                                                const WebString& value,
                                                const WebString& label,
                                                int item_id,
                                                unsigned index) {
  DCHECK(node == element_);

  switch (item_id) {
    case WebAutofillClient::MenuItemIDAutocompleteEntry:
    case WebAutofillClient::MenuItemIDPasswordEntry:
      // User selected an Autocomplete or password entry, so we fill directly.
      SetNodeText(value, &element_);
      break;
    case WebAutofillClient::MenuItemIDDataListEntry:
      AcceptDataListSuggestion(value);
      break;
  }
}

void AutofillAgent::didSelectAutofillSuggestion(const WebNode& node,
                                                const WebString& value,
                                                const WebString& label,
                                                int item_id) {
  didClearAutofillSelection(node);
}

void AutofillAgent::didClearAutofillSelection(const WebNode& node) {
}

void AutofillAgent::removeAutocompleteSuggestion(const WebString& name,
                                                 const WebString& value) {
}

void AutofillAgent::textFieldDidEndEditing(const WebInputElement& element) {
}

void AutofillAgent::textFieldDidChange(const WebInputElement& element) {
  if (did_set_node_text_) {
      did_set_node_text_ = false;
      return;
  }

  // We post a task for doing the Autofill as the caret position is not set
  // properly at this point (http://bugs.webkit.org/show_bug.cgi?id=16976) and
  // it is needed to trigger autofill.
  weak_ptr_factory_.InvalidateWeakPtrs();
  base::MessageLoop::current()->PostTask(
        FROM_HERE,
        base::Bind(&AutofillAgent::TextFieldDidChangeImpl,
                   weak_ptr_factory_.GetWeakPtr(), element));
}

void AutofillAgent::TextFieldDidChangeImpl(const WebInputElement& element) {
  ShowSuggestions(element, false, true, false);
}

void AutofillAgent::textFieldDidReceiveKeyDown(const WebInputElement& element,
                                               const WebKeyboardEvent& event) {
  if (event.windowsKeyCode == ui::VKEY_DOWN ||
      event.windowsKeyCode == ui::VKEY_UP)
    ShowSuggestions(element, true, true, true);
}

void AutofillAgent::ShowSuggestions(const WebInputElement& element,
                                    bool autofill_on_empty_values,
                                    bool requires_caret_at_end,
                                    bool display_warning_if_disabled) {
  if (!element.isEnabled() || element.isReadOnly() || !element.isTextField() ||
      element.isPasswordField() || !element.suggestedValue().isEmpty())
    return;

  // Don't attempt to autofill with values that are too large or if filling
  // criteria are not met.
  WebString value = element.editingValue();
  if (value.length() > kMaximumTextSizeForAutofill ||
      (!autofill_on_empty_values && value.isEmpty()) ||
      (requires_caret_at_end &&
       (element.selectionStart() != element.selectionEnd() ||
        element.selectionEnd() != static_cast<int>(value.length())))) {
    // Any popup currently showing is obsolete.
    WebKit::WebView* web_view = render_view()->GetWebView();
    if (web_view)
      web_view->hidePopups();

    return;
  }

  element_ = element;

  // TODO: Currently node-webkit didn't support HTML5 attribute |autocomplete|
  // of the |input|.
  std::vector<string16> v;
  std::vector<string16> l;
  std::vector<string16> i;
  std::vector<int> ids;
  AppendDataListSuggestions(element, &v, &l, &i, &ids);

  WebKit::WebView* web_view = render_view()->GetWebView();
  if (!web_view)
    return;

  if (v.empty()) {
    // No suggestions, any popup currently showing is obsolete.
    web_view->hidePopups();
    return;
  }

  // Send to WebKit for display.
  web_view->applyAutofillSuggestions(element, v, l, i, ids);
}

void AutofillAgent::AcceptDataListSuggestion(const string16& suggested_value) {
  string16 new_value = suggested_value;
  // If this element takes multiple values then replace the last part with
  // the suggestion.
  if (element_.isMultiple() &&
      element_.formControlType() == WebString::fromUTF8("email")) {
    std::vector<string16> parts;

    base::SplitStringDontTrim(element_.editingValue(), ',', &parts);
    if (parts.size() == 0)
      parts.push_back(string16());

    string16 last_part = parts.back();
    // We want to keep just the leading whitespace.
    for (size_t i = 0; i < last_part.size(); ++i) {
      if (!IsWhitespace(last_part[i])) {
        last_part = last_part.substr(0, i);
        break;
      }
    }
    last_part.append(suggested_value);
    parts[parts.size() - 1] = last_part;

    new_value = JoinString(parts, ',');
  }
  SetNodeText(new_value, &element_);
}

void AutofillAgent::SetNodeText(const string16& value,
                                WebKit::WebInputElement* node) {
  did_set_node_text_ = true;
  string16 substring = value;
  substring = substring.substr(0, node->maxLength());

  node->setEditingValue(substring);
}

}  // namespace nw


<?xml version="1.0" ?>
<!DOCTYPE translationbundle>
<translationbundle lang="it">
<translation id="9065203028668620118">Modifica</translation>
<translation id="5230516054153933099">Finestra</translation>
<translation id="7772127298218883077">Informazioni su <ph name="PRODUCT_NAME"/></translation>
<translation id="3315158641124845231">Nascondi <ph name="PRODUCT_NAME"/></translation>
<translation id="881799181680267069">Nascondi altre</translation>
<translation id="923467487918828349">Mostra tutte</translation>
<translation id="4708849949179781599">Esci da <ph name="PRODUCT_NAME"/></translation>
<translation id="2079545284768500474">Annulla</translation>
<translation id="6556866813142980365">Ripeti</translation>
<translation id="7724603315864178912">Taglia</translation>
<translation id="6896758677409633944">Copia</translation>
<translation id="3943857333388298514">Incolla</translation>
<translation id="8261506727792406068">Elimina</translation>
<translation id="5037676449506322593">Seleziona tutto</translation>
<translation id="5941711191222866238">Riduci a icona</translation>
<translation id="2498539833203011245">Riduci a icona</translation>
<translation id="3265459715026181080">Chiudi finestra</translation>
<translation id="6315493146179903667">Porta tutto in primo piano</translation>
</translationbundle>


/******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};

/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {

/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId])
/******/ 			return installedModules[moduleId].exports;

/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			exports: {},
/******/ 			id: moduleId,
/******/ 			loaded: false
/******/ 		};

/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

/******/ 		// Flag the module as loaded
/******/ 		module.loaded = true;

/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}

/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;

/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;

/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";

/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports) {

	/**
	 * Copyright (c) 2015-present, Facebook, Inc.
	 * All rights reserved.
	 *
	 * This source code is licensed under the BSD-style license found in the
	 * LICENSE file in the root directory of this source tree. An additional grant
	 * of patent rights can be found in the PATENTS file in the same directory.
	 *
	 *
	 */
	'use strict';

	/* global chrome */

	// proxy from main page to devtools (via the background page)

	var port = chrome.runtime.connect({
	  name: 'content-script'
	});

	port.onMessage.addListener(handleMessageFromDevtools);
	port.onDisconnect.addListener(handleDisconnect);
	window.addEventListener('message', handleMessageFromPage);

	window.postMessage({
	  source: 'react-devtools-content-script',
	  hello: true
	}, '*');

	function handleMessageFromDevtools(message) {
	  window.postMessage({
	    source: 'react-devtools-content-script',
	    payload: message
	  }, '*');
	}

	function handleMessageFromPage(evt) {
	  if (evt.data && evt.data.source === 'react-devtools-bridge') {
	    // console.log('page -> rep -> dev', evt.data);
	    port.postMessage(evt.data.payload);
	  }
	}

	function handleDisconnect() {
	  window.removeEventListener('message', handleMessageFromPage);
	  window.postMessage({
	    source: 'react-devtools-content-script',
	    payload: {
	      type: 'event',
	      evt: 'shutdown'
	    }
	  }, '*');
	}

/***/ }
/******/ ]);


keep keep keep add keep keep keep keep keep

throw new Error('mixed_context should be set with new_instance in nw.Window.open');


language: node_js
node_js:
  - 0.10
  - 0.11


Copyright (c) 2010-2014 Caolan McMahon

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


<script>
  nw.Window.get().getPrinters(printers => { console.log(printers);});
  nw.Window.get().showDevTools();
</script>


first running `meteor add user:package`.

* iOS icons and launch screens have been updated to support iOS 11

[Issue #9236] Allow Npm.depends to specify any http or https URL [#9236](https://github.com/meteor/meteor/issues/9236)

var selftest = require('../tool-testing/selftest.js');
var utils = require('../utils/utils.js');

import { sha1 } from '../fs/watch.js';
import httpHelpers from '../utils/http-helpers';

selftest.define('subset generator', function () {
  var out = [];
  utils.generateSubsetsOfIncreasingSize(['a', 'b', 'c'], function (x) {
    out.push(x);
  });
  selftest.expectEqual(out, [
    [],
    [ 'a' ],
    [ 'b' ],
    [ 'c' ],
    [ 'a', 'b' ],
    [ 'a', 'c' ],
    [ 'b', 'c' ],
    [ 'a', 'b', 'c' ]
  ]);
  out = [];
  utils.generateSubsetsOfIncreasingSize(['a', 'b', 'c'], function (x) {
    out.push(x);
    if (x[1] === 'c') {
      // stop iterating
      return true;
    }
  });
  selftest.expectEqual(out, [
    [],
    [ 'a' ],
    [ 'b' ],
    [ 'c' ],
    [ 'a', 'b' ],
    [ 'a', 'c' ]
  ]);
});

selftest.define("url has scheme", function () {
  // URL scheme must start with a letter, and then can be followed by
  // any number of alphanumerics, +, -, . RFC 2396 Appendix A.
  selftest.expectEqual(utils.hasScheme("http://example.com"), true);
  selftest.expectEqual(utils.hasScheme("https://example.com"), true);
  selftest.expectEqual(utils.hasScheme("ddp://example.com"), true);
  selftest.expectEqual(utils.hasScheme("http://example.com:80"), true);
  selftest.expectEqual(utils.hasScheme("https://example.com:443"), true);
  selftest.expectEqual(utils.hasScheme("ddp://example.com:443"), true);
  selftest.expectEqual(utils.hasScheme("ddp://example"), true);
  selftest.expectEqual(utils.hasScheme("ddp://"), true);
  selftest.expectEqual(utils.hasScheme("example.comhttp://"), true);
  selftest.expectEqual(utils.hasScheme("example+com+http://"), true);
  selftest.expectEqual(utils.hasScheme("example-com+http://"), true);

  selftest.expectEqual(utils.hasScheme("example"), false);
  selftest.expectEqual(utils.hasScheme("example.com"), false);
  selftest.expectEqual(utils.hasScheme("example.com:443"), false);
  selftest.expectEqual(utils.hasScheme("http:/example"), false);
  selftest.expectEqual(utils.hasScheme("http:example"), false);
  selftest.expectEqual(utils.hasScheme("example_com+http://"), false);
});

selftest.define("parse url", function () {
  selftest.expectEqual(utils.parseUrl("http://localhost:3000"), {
    hostname: "localhost",
    port: "3000",
    protocol: "http"
  });
  selftest.expectEqual(utils.parseUrl("https://localhost:3000"), {
    hostname: "localhost",
    port: "3000",
    protocol: "https"
  });
  selftest.expectEqual(utils.parseUrl("localhost:3000"), {
    hostname: "localhost",
    port: "3000",
    protocol: undefined
  });
  selftest.expectEqual(utils.parseUrl("3000"), {
    hostname: undefined,
    port: "3000",
    protocol: undefined
  });
  selftest.expectEqual(utils.parseUrl("3000example.com:3000"), {
    hostname: "3000example.com",
    port: "3000",
    protocol: undefined
  });
  selftest.expectEqual(utils.parseUrl("http://example.com:3000"), {
    hostname: "example.com",
    port: "3000",
    protocol: "http"
  });
  selftest.expectEqual(utils.parseUrl("https://example.com:3000"), {
    hostname: "example.com",
    port: "3000",
    protocol: "https"
  });
  selftest.expectEqual(utils.parseUrl("example.com:3000"), {
    hostname: "example.com",
    port: "3000",
    protocol: undefined
  });
  selftest.expectEqual(utils.parseUrl("127.0.0.1:3000"), {
    hostname: "127.0.0.1",
    port: "3000",
    protocol: undefined
  });
  selftest.expectEqual(utils.parseUrl("[::]:3000"), {
    hostname: "::",
    port: "3000",
    protocol: undefined
  });
  selftest.expectEqual(utils.parseUrl("http://[::]:3000"), {
    hostname: "::",
    port: "3000",
    protocol: "http"
  });
  selftest.expectEqual(utils.parseUrl("https://[::]:3000"), {
    hostname: "::",
    port: "3000",
    protocol: "https"
  });
  selftest.expectEqual(utils.parseUrl("[0000:0000:0000:0000:0000:0000:0000:0001]:3000"), {
    hostname: "0000:0000:0000:0000:0000:0000:0000:0001",
    port: "3000",
    protocol: undefined
  });

  // tests for defaults
  selftest.expectEqual(utils.parseUrl("http://example.com:3000", {
    hostname: "foo.com",
    port: "4000",
    protocol: "https"
  }), {
    hostname: "example.com",
    port: "3000",
    protocol: "http"
  });
  selftest.expectEqual(utils.parseUrl("example.com:3000", {
    port: "4000",
    protocol: "https"
  }), {
    hostname: "example.com",
    port: "3000",
    protocol: "https"
  });
  selftest.expectEqual(utils.parseUrl("3000", {
    port: "4000",
    protocol: "https",
    hostname: "example.com"
  }), {
    hostname: "example.com",
    port: "3000",
    protocol: "https"
  });
});

selftest.define("resume downloads", ['net'], function () {
  // A reasonably big file that (I think) should take more than 1s to download
  // and that we know the size of
  const url = 'https://warehouse.meteor.com/builds/EXSxwGqYjjJKh3WMJ/1467929945102/DRyKg3bYHL/babel-compiler-6.8.4-os+web.browser+web.cordova.tgz';

  let interruptCount = 0;
  let bytesSinceLastInterrupt = 0;

  const resumedPromise = Promise.resolve().then(
    () => httpHelpers.getUrlWithResuming({
      url: url,
      encoding: null,
      retryDelaySecs: 1,
      onRequest(request) {
        request.on("data", chunk => {
          bytesSinceLastInterrupt += chunk.length
          if (bytesSinceLastInterrupt > 500000) {
            bytesSinceLastInterrupt = 0;
            ++interruptCount;
            request.emit('error', 'pretend-http-error');
            request.emit('end');
            request.abort();
          }
        });
      }
    })
  );

  const normalPromise = Promise.resolve().then(
    () => httpHelpers.getUrl({
      url,
      encoding: null,
    })
  );

  Promise.all([
    resumedPromise,
    normalPromise,
  ]).then(bodies => {
    selftest.expectTrue(interruptCount > 1);

    selftest.expectEqual(
      bodies[0].length,
      bodies[1].length
    );

    selftest.expectEqual(
      sha1(bodies[0]),
      sha1(bodies[1])
    );
  }).await();
});


import {basename} from "path"
import {readFileSync as readFile} from "fs"
import * as acorn from "acorn"

let infile, forceFile, silent = false, compact = false, tokenize = false
const options = {}

function help(status) {
  const print = (status == 0) ? console.log : console.error
  print("usage: " + basename(process.argv[1]) + " [--ecma3|--ecma5|--ecma6|--ecma7|...|--ecma2015|--ecma2016|...]")
  print("        [--tokenize] [--locations] [---allow-hash-bang] [--compact] [--silent] [--module] [--help] [--] [infile]")
  process.exit(status)
}

for (let i = 2; i < process.argv.length; ++i) {
  const arg = process.argv[i]
  if ((arg == "-" || arg[0] != "-") && !infile) infile = arg
  else if (arg == "--" && !infile && i + 2 == process.argv.length) forceFile = infile = process.argv[++i]
  else if (arg == "--locations") options.locations = true
  else if (arg == "--allow-hash-bang") options.allowHashBang = true
  else if (arg == "--silent") silent = true
  else if (arg == "--compact") compact = true
  else if (arg == "--help") help(0)
  else if (arg == "--tokenize") tokenize = true
  else if (arg == "--module") options.sourceType = 'module'
  else {
    let match = arg.match(/^--ecma(\d+)$/)
    if (match)
      options.ecmaVersion = +match[1]
    else
      help(1)
  }
}

function run(code) {
  let result
  if (!tokenize) {
    try { result = acorn.parse(code, options) }
    catch(e) { console.error(e.message); process.exit(1) }
  } else {
    result = []
    let tokenizer = acorn.tokenizer(code, options), token
    while (true) {
      try { token = tokenizer.getToken() }
      catch(e) { console.error(e.message); process.exit(1) }
      result.push(token)
      if (token.type == acorn.tokTypes.eof) break
    }
  }
  if (!silent) console.log(JSON.stringify(result, null, compact ? null : 2))
}

if (forceFile || infile && infile != "-") {
  run(readFile(infile, "utf8"))
} else {
  let code = ""
  process.stdin.resume()
  process.stdin.on("data", chunk => code += chunk)
  process.stdin.on("end", () => run(code))
}