var path = require('path');
var fs = require('fs');
var assert = require('assert');
var os = require('os');
var child_process = require('child_process');

exports.testDir = path.dirname(__filename);
exports.fixturesDir = path.join(exports.testDir, 'fixtures');
exports.libDir = path.join(exports.testDir, '../lib');
exports.tmpDirName = 'tmp';
exports.PORT = +process.env.NODE_COMMON_PORT || 12346;

if (process.env.TEST_THREAD_ID) {
  // Distribute ports in parallel tests
  if (!process.env.NODE_COMMON_PORT)
    exports.PORT += +process.env.TEST_THREAD_ID * 100;

  exports.tmpDirName += '.' + process.env.TEST_THREAD_ID;
}
exports.tmpDir = path.join(exports.testDir, exports.tmpDirName);

var opensslCli = null;

// opensslCli defined lazily to reduce overhead of spawnSync
Object.defineProperty(exports, 'opensslCli', {get: function() {
  if (opensslCli !== null) return opensslCli;

  if (process.config.variables.node_shared_openssl) {
    // use external command
    opensslCli = 'openssl';
  } else {
    // use command built from sources included in io.js repository
    opensslCli = path.join(path.dirname(process.execPath), 'openssl-cli');
  }

  if (process.platform === 'win32') opensslCli += '.exe';

  var openssl_cmd = child_process.spawnSync(opensslCli, ['version']);
  if (openssl_cmd.status !== 0 || openssl_cmd.error !== undefined) {
    // openssl command cannot be executed
    opensslCli = false;
  }
  return opensslCli;
}, enumerable: true });

Object.defineProperty(exports, 'hasCrypto', {get: function() {
  return process.versions.openssl ? true : false;
}});

if (process.platform === 'win32') {
  exports.PIPE = '\\\\.\\pipe\\libuv-test';
} else {
  exports.PIPE = exports.tmpDir + '/test.sock';
}

if (process.env.NODE_COMMON_PIPE) {
  exports.PIPE = process.env.NODE_COMMON_PIPE;
  // Remove manually, the test runner won't do it
  // for us like it does for files in test/tmp.
  try {
    fs.unlinkSync(exports.PIPE);
  } catch (e) {
    // Ignore.
  }
}

if (process.platform === 'win32') {
  exports.faketimeCli = false;
} else {
  exports.faketimeCli = path.join(__dirname, "..", "tools", "faketime", "src",
    "faketime");
}

var ifaces = os.networkInterfaces();
exports.hasIPv6 = Object.keys(ifaces).some(function(name) {
  return /lo/.test(name) && ifaces[name].some(function(info) {
    return info.family === 'IPv6';
  });
});

var util = require('util');
for (var i in util) exports[i] = util[i];
//for (var i in exports) global[i] = exports[i];

function protoCtrChain(o) {
  var result = [];
  for (; o; o = o.__proto__) { result.push(o.constructor); }
  return result.join();
}

exports.indirectInstanceOf = function(obj, cls) {
  if (obj instanceof cls) { return true; }
  var clsChain = protoCtrChain(cls.prototype);
  var objChain = protoCtrChain(obj);
  return objChain.slice(-clsChain.length) === clsChain;
};

exports.ddCommand = function(filename, kilobytes) {
  if (process.platform === 'win32') {
    var p = path.resolve(exports.fixturesDir, 'create-file.js');
    return '"' + process.argv[0] + '" "' + p + '" "' +
           filename + '" ' + (kilobytes * 1024);
  } else {
    return 'dd if=/dev/zero of="' + filename + '" bs=1024 count=' + kilobytes;
  }
};

exports.spawnCat = function(options) {
  var spawn = require('child_process').spawn;

  if (process.platform === 'win32') {
    return spawn('more', [], options);
  } else {
    return spawn('cat', [], options);
  }
};

exports.spawnSyncCat = function(options) {
  var spawnSync = require('child_process').spawnSync;

  if (process.platform === 'win32') {
    return spawnSync('more', [], options);
  } else {
    return spawnSync('cat', [], options);
  }
};

exports.spawnPwd = function(options) {
  var spawn = require('child_process').spawn;

  if (process.platform === 'win32') {
    return spawn('cmd.exe', ['/c', 'cd'], options);
  } else {
    return spawn('pwd', [], options);
  }
};

var knownGlobals = [setTimeout,
                    setInterval,
                    setImmediate,
                    clearTimeout,
                    clearInterval,
                    clearImmediate,
                    console,
                    constructor, // Enumerable in V8 3.21.
                    Buffer,
                    process,
                    global];

if (global.gc) {
  knownGlobals.push(gc);
}

if (global.DTRACE_HTTP_SERVER_RESPONSE) {
  knownGlobals.push(DTRACE_HTTP_SERVER_RESPONSE);
  knownGlobals.push(DTRACE_HTTP_SERVER_REQUEST);
  knownGlobals.push(DTRACE_HTTP_CLIENT_RESPONSE);
  knownGlobals.push(DTRACE_HTTP_CLIENT_REQUEST);
  knownGlobals.push(DTRACE_NET_STREAM_END);
  knownGlobals.push(DTRACE_NET_SERVER_CONNECTION);
}

if (global.COUNTER_NET_SERVER_CONNECTION) {
  knownGlobals.push(COUNTER_NET_SERVER_CONNECTION);
  knownGlobals.push(COUNTER_NET_SERVER_CONNECTION_CLOSE);
  knownGlobals.push(COUNTER_HTTP_SERVER_REQUEST);
  knownGlobals.push(COUNTER_HTTP_SERVER_RESPONSE);
  knownGlobals.push(COUNTER_HTTP_CLIENT_REQUEST);
  knownGlobals.push(COUNTER_HTTP_CLIENT_RESPONSE);
}

if (global.LTTNG_HTTP_SERVER_RESPONSE) {
  knownGlobals.push(LTTNG_HTTP_SERVER_RESPONSE);
  knownGlobals.push(LTTNG_HTTP_SERVER_REQUEST);
  knownGlobals.push(LTTNG_HTTP_CLIENT_RESPONSE);
  knownGlobals.push(LTTNG_HTTP_CLIENT_REQUEST);
  knownGlobals.push(LTTNG_NET_STREAM_END);
  knownGlobals.push(LTTNG_NET_SERVER_CONNECTION);
}

if (global.ArrayBuffer) {
  knownGlobals.push(ArrayBuffer);
  knownGlobals.push(Int8Array);
  knownGlobals.push(Uint8Array);
  knownGlobals.push(Uint8ClampedArray);
  knownGlobals.push(Int16Array);
  knownGlobals.push(Uint16Array);
  knownGlobals.push(Int32Array);
  knownGlobals.push(Uint32Array);
  knownGlobals.push(Float32Array);
  knownGlobals.push(Float64Array);
  knownGlobals.push(DataView);
}

// Harmony features.
if (global.Proxy) {
  knownGlobals.push(Proxy);
}

if (global.Symbol) {
  knownGlobals.push(Symbol);
}

function leakedGlobals() {
  var leaked = [];

  for (var val in global)
    if (-1 === knownGlobals.indexOf(global[val]))
      leaked.push(val);

  return leaked;
};
exports.leakedGlobals = leakedGlobals;

// Turn this off if the test should not check for global leaks.
exports.globalCheck = false;

process.on('exit', function() {
  if (!exports.globalCheck) return;
  var leaked = leakedGlobals();
  if (leaked.length > 0) {
    console.error('Unknown globals: %s', leaked);
    assert.ok(false, 'Unknown global found');
  }
});

var mustCallChecks = [];

function runCallChecks(exitCode) {
  if (exitCode !== 0) return;

  var failed = mustCallChecks.filter(function(context) {
    return context.actual !== context.expected;
  });

  failed.forEach(function(context) {
    console.log('Mismatched %s function calls. Expected %d, actual %d.',
                context.name,
                context.expected,
                context.actual);
    console.log(context.stack.split('\n').slice(2).join('\n'));
  });

  if (failed.length) process.exit(1);
}

exports.mustCall = function(fn, expected) {
  if (typeof expected !== 'number') expected = 1;

  var context = {
    expected: expected,
    actual: 0,
    stack: (new Error).stack,
    name: fn.name || '<anonymous>'
  };

  // add the exit listener only once to avoid listener leak warnings
  if (mustCallChecks.length === 0) process.on('exit', runCallChecks);

  mustCallChecks.push(context);

  return function() {
    context.actual++;
    return fn.apply(this, arguments);
  };
};

exports.checkSpawnSyncRet = function(ret) {
  assert.strictEqual(ret.status, 0);
  assert.strictEqual(ret.error, undefined);
};

var etcServicesFileName = path.join('/etc', 'services');
if (process.platform === 'win32') {
  etcServicesFileName = path.join(process.env.SystemRoot, 'System32', 'drivers',
    'etc', 'services');
}

/*
 * Returns a string that represents the service name associated
 * to the service bound to port "port" and using protocol "protocol".
 *
 * If the service is not defined in the services file, it returns
 * the port number as a string.
 *
 * Returns undefined if /etc/services (or its equivalent on non-UNIX
 * platforms) can't be read.
 */
exports.getServiceName = function getServiceName(port, protocol) {
  if (port == null) {
    throw new Error("Missing port number");
  }

  if (typeof protocol !== 'string') {
    throw new Error("Protocol must be a string");
  }

  /*
   * By default, if a service can't be found in /etc/services,
   * its name is considered to be its port number.
   */
  var serviceName = port.toString();

  try {
    /*
     * I'm not a big fan of readFileSync, but reading /etc/services asynchronously
     * here would require implementing a simple line parser, which seems overkill
     * for a simple utility function that is not running concurrently with any
     * other one.
     */
    var servicesContent = fs.readFileSync(etcServicesFileName,
      { encoding: 'utf8'});
    var regexp = util.format('^(\\w+)\\s+\\s%d/%s\\s', port, protocol);
    var re = new RegExp(regexp, 'm');

    var matches = re.exec(servicesContent);
    if (matches && matches.length > 1) {
      serviceName = matches[1];
    }
  } catch(e) {
    console.error('Cannot read file: ', etcServicesFileName);
    return undefined;
  }

  return serviceName;
}

exports.hasMultiLocalhost = function hasMultiLocalhost() {
  var TCP = process.binding('tcp_wrap').TCP;
  var t = new TCP();
  var ret = t.bind('127.0.0.2', exports.PORT);
  t.close();
  return ret === 0;
};

exports.isValidHostname = function(str) {
  // See http://stackoverflow.com/a/3824105
  var re = new RegExp(
    '^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])' +
    '(\\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9]))*$');

  return !!str.match(re) && str.length <= 255;
}

exports.fileExists = function(pathname) {
  try {
    fs.accessSync(pathname);
    return true;
  } catch (err) {
    return false;
  }
};

exports.done = function(n, cb) {
  var doneCounter = 0;
  return function () {
    doneCounter++;
    if (doneCounter >= n)
      cb();
  };
};


return g_reloading_app;
}
} //namespace nw

void OverrideWebkitPrefsHook(content::RenderViewHost* rvh, content::WebPreferences* web_prefs) {

if (package->root()->GetDictionary(switches::kmWebkit, &webkit)) {


}
namespace content {
class RenderFrame;
class RenderProcessHost;
class NotificationDetails;
class RenderView;
class WebContents;

#include "extensions/browser/app_window/app_window.h"

#include "nw_content.h"
#include "nw_base.h"

#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/json/json_reader.h"
#include "base/path_service.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_restrictions.h"

#include "chrome/browser/browser_process.h"
#include "chrome/browser/io_thread.h"
#include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/chrome_paths.h"

#include "components/crx_file/id_util.h"

#include "content/browser/dom_storage/dom_storage_area.h"
#include "content/common/dom_storage/dom_storage_map.h"
#include "content/nw/src/common/shell_switches.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/result_codes.h"
#include "content/public/common/user_agent.h"
#include "content/public/renderer/render_view.h"

#include "content/nw/src/nw_content_verifier_delegate.h"
#include "content/nw/src/api/menu/menu.h"
#include "content/nw/src/api/object_manager.h"
#include "content/nw/src/policy_cert_verifier.h"

#include "extensions/browser/event_router.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/renderer/dispatcher.h"
#include "extensions/renderer/script_context.h"
#include "extensions/common/extension.h"
#include "extensions/common/feature_switch.h"
#include "extensions/common/features/feature.h"
#include "extensions/common/manifest.h"
#include "extensions/common/manifest_constants.h"

#include "extensions/grit/extensions_renderer_resources.h"

#include "net/cert/x509_certificate.h"

#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebFrame.h"
#include "third_party/WebKit/public/web/WebLocalFrame.h"
#include "third_party/WebKit/public/web/WebSecurityPolicy.h"
#include "third_party/WebKit/public/web/WebView.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebLocalFrame.h"
#include "third_party/WebKit/public/web/WebScriptSource.h"

#include "chrome/common/chrome_constants.h"

#include "ui/base/resource/resource_bundle.h"
#include "ui/base/ui_base_types.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_skia_rep.h"
#include "ui/gfx/codec/png_codec.h"

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

#include "third_party/node/src/node_webkit.h"
#include "third_party/WebKit/public/web/WebScopedMicrotaskSuppression.h"
#include "nw/id/commit.h"
#include "content/nw/src/nw_version.h"

#undef LOG
#undef ASSERT
#undef FROM_HERE

#if defined(OS_WIN)
#define _USE_MATH_DEFINES
#include <math.h>
#endif
#include "third_party/WebKit/Source/config.h"
#include "third_party/WebKit/Source/core/frame/Frame.h"
#include "third_party/WebKit/Source/web/WebLocalFrameImpl.h"
#include "V8HTMLElement.h"

#include "content/renderer/render_view_impl.h"
#include "base/logging.h"

using content::RenderView;
using content::RenderViewImpl;
using extensions::ScriptContext;
using extensions::Extension;
using extensions::EventRouter;
using extensions::Manifest;
using extensions::Feature;
using extensions::ExtensionPrefs;
using extensions::ExtensionRegistry;
using extensions::Dispatcher;
using extensions::ContentVerifierDelegate;
using extensions::NWContentVerifierDelegate;
using blink::WebScriptSource;

namespace manifest_keys = extensions::manifest_keys;

CallTickCallbackFn g_call_tick_callback_fn = nullptr;
SetupNWNodeFn g_setup_nwnode_fn = nullptr;
IsNodeInitializedFn g_is_node_initialized_fn = nullptr;
SetNWTickCallbackFn g_set_nw_tick_callback_fn = nullptr;
StartNWInstanceFn g_start_nw_instance_fn = nullptr;
GetNodeContextFn g_get_node_context_fn = nullptr;
SetNodeContextFn g_set_node_context_fn = nullptr;
GetNodeEnvFn g_get_node_env_fn = nullptr;
GetCurrentEnvironmentFn g_get_current_env_fn = nullptr;
EmitExitFn g_emit_exit_fn = nullptr;
RunAtExitFn g_run_at_exit_fn = nullptr;

namespace nw {

namespace {

extensions::Dispatcher* g_dispatcher = NULL;
bool g_reloading_app = 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::Handle<v8::Value> CallNWTickCallback(void* env, const v8::Handle<v8::Value> ret) {
  blink::WebScopedMicrotaskSuppression suppression;
  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::Handle<v8::Object> global(context->v8_context()->Global());
  v8::Handle<v8::Value> nw(global->Get(nw_string));
  if (nw->IsUndefined()) {
    nw = v8::Object::New(context->isolate());;
    //node_context->Enter();
    global->Set(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>();
}

} //namespace

bool GetPackageImage(nw::Package* package,
                     const FilePath& icon_path,
                     gfx::Image* image) {
  FilePath path = package->ConvertToAbsoutePath(icon_path);

  // Read the file from disk.
  std::string file_contents;
  if (path.empty() || !base::ReadFileToString(path, &file_contents))
    return false;

  // Decode the bitmap using WebKit's image decoder.
  const unsigned char* data =
      reinterpret_cast<const unsigned char*>(file_contents.data());
  scoped_ptr<SkBitmap> decoded(new SkBitmap());
  // Note: This class only decodes bitmaps from extension resources. Chrome
  // doesn't (for security reasons) directly load extension resources provided
  // by the extension author, but instead decodes them in a separate
  // locked-down utility process. Only if the decoding succeeds is the image
  // saved from memory to disk and subsequently used in the Chrome UI.
  // Chrome is therefore decoding bitmaps here that were generated by Chrome.
  gfx::PNGCodec::Decode(data, file_contents.length(), decoded.get());
  if (decoded->empty())
    return false;  // Unable to decode.

  *image = gfx::Image::CreateFrom1xBitmap(*decoded);
  return true;
}

int MainPartsPreCreateThreadsHook() {
  base::ThreadRestrictions::ScopedAllowIO allow_io;
  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
  nw::Package* package = InitNWPackage();
  if (package && !package->path().empty()) {
    base::FilePath path = package->path().NormalizePathSeparators();

    command_line->AppendSwitchPath("nwapp", path);
    int dom_storage_quota_mb;
    if (package->root()->GetInteger("dom_storage_quota", &dom_storage_quota_mb)) {
      content::DOMStorageMap::SetQuotaOverride(dom_storage_quota_mb * 1024 * 1024);
    }

    base::FilePath user_data_dir;
    std::string name;
    package->root()->GetString("name", &name);
    if (!name.empty() && PathService::Get(chrome::DIR_USER_DATA, &user_data_dir)) {
      base::FilePath old_dom_storage = user_data_dir
        .Append(FILE_PATH_LITERAL("Local Storage"))
        .Append(FILE_PATH_LITERAL("file__0.localstorage"));
      if (base::PathExists(old_dom_storage)) {
        std::string id = crx_file::id_util::GenerateId(name);
        GURL origin("chrome-extension://" + id + "/");
        base::FilePath new_storage_dir = user_data_dir.Append(FILE_PATH_LITERAL("Default"))
          .Append(FILE_PATH_LITERAL("Local Storage"));
        base::CreateDirectory(new_storage_dir);

        base::FilePath new_dom_storage = new_storage_dir
          .Append(content::DOMStorageArea::DatabaseFileNameFromOrigin(origin));
        base::FilePath new_dom_journal = new_dom_storage.ReplaceExtension(FILE_PATH_LITERAL("localstorage-journal"));
        base::FilePath old_dom_journal = old_dom_storage.ReplaceExtension(FILE_PATH_LITERAL("localstorage-journal"));
        base::Move(old_dom_journal, new_dom_journal);
        base::Move(old_dom_storage, new_dom_storage);
        LOG_IF(INFO, true) << "Migrate DOM storage from " << old_dom_storage.AsUTF8Unsafe() << " to " << new_dom_storage.AsUTF8Unsafe();
      }
    }

    const base::ListValue *additional_trust_anchors = NULL;
    if (package->root()->GetList("additional_trust_anchors", &additional_trust_anchors)) {
      net::CertificateList trust_anchors;
      for (size_t i=0; i<additional_trust_anchors->GetSize(); i++) {
        std::string certificate_string;
        if (!additional_trust_anchors->GetString(i, &certificate_string)) {
          // LOG(WARNING)
          //   << "Could not get string from entry " << i;
          continue;
        }

        net::CertificateList loaded =
          net::X509Certificate::CreateCertificateListFromBytes(
              certificate_string.c_str(), certificate_string.size(),
              net::X509Certificate::FORMAT_AUTO);
        if (loaded.empty() && !certificate_string.empty()) {
          // LOG(WARNING)
          //   << "Could not load certificate from entry " << i;
          continue;
        }

        trust_anchors.insert(trust_anchors.end(), loaded.begin(), loaded.end());
      }
      if (!trust_anchors.empty()) {
        // LOG(INFO)
        //   << "Added " << trust_anchors.size() << " certificates to trust anchors.";
        PolicyCertVerifier* verifier =
          (PolicyCertVerifier*)g_browser_process->io_thread()->globals()->cert_verifier.get();
        verifier->SetTrustAnchors(trust_anchors);
      }
    }
  }
  return content::RESULT_CODE_NORMAL_EXIT;
}

void MainPartsPostDestroyThreadsHook() {
  ReleaseNWPackage();
}

void DocumentFinishHook(blink::WebFrame* frame,
                         const extensions::Extension* extension,
                         const GURL& effective_document_url) {
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  v8::HandleScope hscope(isolate);
  std::string path = effective_document_url.path();
  v8::Local<v8::Context> v8_context = frame->mainWorldScriptContext();
  std::string root_path = extension->path().AsUTF8Unsafe();
  base::FilePath root(extension->path());
  RenderViewImpl* rv = RenderViewImpl::FromWebView(frame->view());
  if (!rv)
    return;
  std::string js_fn = rv->renderer_preferences().nw_inject_js_doc_end;
  if (js_fn.empty())
    return;
  base::FilePath js_file = root.AppendASCII(js_fn);
  std::string content;
  if (!base::ReadFileToString(js_file, &content)) {
    //LOG(WARNING) << "Failed to load js script file: " << js_file.value();
    return;
  }
  base::string16 jscript = base::UTF8ToUTF16(content);
  {
    blink::WebScopedMicrotaskSuppression suppression;
    v8::Context::Scope cscope(v8_context);
    // v8::Handle<v8::Value> result;
    frame->executeScriptAndReturnValue(WebScriptSource(jscript));
  }
}

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::WebLocalFrame* web_frame = frame->GetWebFrame();
  GURL frame_url = ScriptContext::GetDataSourceURLForFrame(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);
  v8::Local<v8::Context> v8_context = frame->GetRenderView()
      ->GetWebView()->mainFrame()->mainWorldScriptContext();
  ScriptContext* script_context =
      dispatcher->script_context_set().GetByV8Context(v8_context);
  if (!script_context || !script_context->extension())
    return;
  if (script_context->extension()->GetType() == Manifest::TYPE_NWJS_APP &&
      script_context->context_type() == Feature::BLESSED_EXTENSION_CONTEXT) {
    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);
    script_context->module_system()->CallModuleMethod("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
  GURL frame_url = ScriptContext::GetDataSourceURLForFrame(frame);
  if (frame->parent() && (!frame_url.is_valid() || frame_url.is_empty()))
    return;
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  v8::HandleScope hscope(isolate);
  frame->document().securityOrigin().grantUniversalAccess();
  frame->setNodeJS(true);
  std::string path = effective_document_url.path();
  v8::Local<v8::Context> v8_context = frame->mainWorldScriptContext();
  std::string root_path = extension->path().AsUTF8Unsafe();
  base::FilePath root(extension->path());
  if (!v8_context.IsEmpty()) {
    blink::WebScopedMicrotaskSuppression suppression;
    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::Local<v8::Script> script2 = v8::Script::Compile(v8::String::NewFromUtf8(isolate, (
        "if (typeof nw != 'undefined' && typeof __filename == 'undefined') {"
        "  var root = '" + root_path + "';"
        "  var path = '" + path      + "';"
        "nw.__filename = root + path;"
        "nw.__dirname = root;"
        "}").c_str()),
    v8::String::NewFromUtf8(isolate, "process_main2"));
    CHECK(*script2);
    script2->Run();
  }
  RenderViewImpl* rv = RenderViewImpl::FromWebView(frame->view());
  if (!rv)
    return;

  ui::ResourceBundle* resource_bundle = &ResourceBundle::GetSharedInstance();
  base::StringPiece resource =
      resource_bundle->GetRawDataResource(IDR_NW_PRE13_SHIM_JS);
  if (resource.empty())
    return;
  base::string16 jscript = base::UTF8ToUTF16(resource.as_string());
  if (!v8_context.IsEmpty()) {
    blink::WebScopedMicrotaskSuppression suppression;
    v8::Context::Scope cscope(v8_context);
    frame->executeScriptAndReturnValue(WebScriptSource(jscript));
  }

  std::string js_fn = rv->renderer_preferences().nw_inject_js_doc_start;
  if (js_fn.empty())
    return;
  base::FilePath js_file = root.AppendASCII(js_fn);
  std::string content;
  if (!base::ReadFileToString(js_file, &content)) {
    //LOG(WARNING) << "Failed to load js script file: " << js_file.value();
    return;
  }
  jscript = base::UTF8ToUTF16(content);
  if (!v8_context.IsEmpty()) {
    blink::WebScopedMicrotaskSuppression suppression;
    v8::Context::Scope cscope(v8_context);
    // v8::Handle<v8::Value> result;
    frame->executeScriptAndReturnValue(WebScriptSource(jscript));
  }
}

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

  bool nodejs_enabled = true;
  context->extension()->manifest()->GetBoolean(manifest_keys::kNWJSEnableNode, &nodejs_enabled);

  if (!nodejs_enabled)
    return;

  if (!g_is_node_initialized_fn())
    g_setup_nwnode_fn(0, nullptr);

  bool mixed_context = false;
  context->extension()->manifest()->GetBoolean(manifest_keys::kNWJSMixedContext, &mixed_context);
  v8::Local<v8::Context> node_context;
  g_get_node_context_fn(&node_context);
  if (node_context.IsEmpty() || mixed_context) {
    {
      int argc = 1;
      char argv0[] = "node";
      char* argv[3];
      argv[0] = argv0;
      argv[1] = argv[2] = nullptr;
      std::string main_fn;

      if (context->extension()->manifest()->GetString(manifest_keys::kNWJSInternalMainFilename, &main_fn)) {
        argc = 2;
        argv[1] = strdup(main_fn.c_str());
      }

      v8::Isolate* isolate = v8::Isolate::GetCurrent();
      v8::HandleScope scope(isolate);
      blink::WebScopedMicrotaskSuppression suppression;

      g_set_nw_tick_callback_fn(&CallNWTickCallback);
      v8::Local<v8::Context> 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"));
      dom_context->Enter();

      g_start_nw_instance_fn(argc, argv, dom_context);
      {
        v8::Local<v8::Script> script =
          v8::Script::Compile(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['chromium'] = '" + chrome::kChromeVersion + "';").c_str()
         ));
        script->Run();
      }

      if (context->extension()->manifest()->GetString(manifest_keys::kNWJSInternalMainFilename, &main_fn)) {
        v8::Local<v8::Script> script = v8::Script::Compile(v8::String::NewFromUtf8(isolate,
         ("global.__filename = '" + main_fn + "';").c_str()));
        script->Run();
      }
      {
        std::string root_path = context->extension()->path().AsUTF8Unsafe();
#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(v8::String::NewFromUtf8(isolate,
         ("global.__dirname = '" + root_path + "';").c_str()));
        script->Run();
      }
      bool content_verification = false;
      if (context->extension()->manifest()->GetBoolean(manifest_keys::kNWJSContentVerifyFlag,
                                                       &content_verification) && content_verification) {
        v8::Local<v8::Script> script =
          v8::Script::Compile(v8::String::NewFromUtf8(isolate,
                                                      (std::string("global.__nwjs_cv = true;") +
                                                       "global.__nwjs_ext_id = '" + context->extension()->id() + "';").c_str()));

        script->Run();
      }

      dom_context->Exit();
    }
  }

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

    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));
#if 1
    v8::Local<v8::Array> symbols = v8::Array::New(isolate, 5);
    symbols->Set(0, v8::String::NewFromUtf8(isolate, "global"));
    symbols->Set(1, v8::String::NewFromUtf8(isolate, "process"));
    symbols->Set(2, v8::String::NewFromUtf8(isolate, "Buffer"));
    symbols->Set(3, v8::String::NewFromUtf8(isolate, "root"));
    symbols->Set(4, v8::String::NewFromUtf8(isolate, "require"));

    g_context->Enter();
    for (unsigned i = 0; i < symbols->Length(); ++i) {
      v8::Local<v8::Value> key = symbols->Get(i);
      v8::Local<v8::Value> val = node_global->Get(key);
      nw->Set(key, val);
    }
    g_context->Exit();
#endif
  }

  std::string set_nw_script;
  if (mixed_context)
    set_nw_script = "var nw = global;";
  {
    blink::WebScopedMicrotaskSuppression suppression;
    v8::Context::Scope cscope(context->v8_context());
    // Make node's relative modules work
    std::string root_path = context->extension()->path().AsUTF8Unsafe();
    GURL frame_url = ScriptContext::GetDataSourceURLForFrame(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::Local<v8::Script> script = v8::Script::Compile(v8::String::NewFromUtf8(isolate, (
        set_nw_script +
        "nw.global.XMLHttpRequest = XMLHttpRequest;" +
        // 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)) {"
        "  var root = '" + root_path + "';"
        "  var 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::String::NewFromUtf8(isolate, "process_main"));
    CHECK(*script);
    script->Run();
  }

}

void AmendManifestContentScriptList(base::DictionaryValue* manifest,
                                    const std::string& name,
                                    const std::string& run_at) {
  base::ListValue* scripts = NULL;
  base::Value* temp_value = NULL;
  bool amend = false;

  if (manifest->Get(manifest_keys::kContentScripts, &temp_value))
      if (temp_value->GetAsList(&scripts))
        amend = true;
  if (!scripts)
    scripts = new base::ListValue();

  std::string js;
  if (manifest->GetString(name, &js)) {
    base::ListValue* js_list = new base::ListValue();
    js_list->Append(new base::StringValue(js));

    base::ListValue* matches = new base::ListValue();
    matches->Append(new base::StringValue("<all_urls>"));

    base::DictionaryValue* content_script = new base::DictionaryValue();
    content_script->Set("js", js_list);
    content_script->Set("matches", matches);
    content_script->SetString("run_at", run_at);
    content_script->SetBoolean("in_main_world", true);

    scripts->Append(content_script);

    if (!amend)
      manifest->Set(manifest_keys::kContentScripts, scripts);
  }
}

void AmendManifestStringList(base::DictionaryValue* manifest,
                   const std::string& path,
                   const std::string& string_value) {
  base::ListValue* pattern_list = NULL;
  base::Value* temp_pattern_value = NULL;
  bool amend = false;

  if (manifest->Get(path, &temp_pattern_value))
      if (temp_pattern_value->GetAsList(&pattern_list))
        amend = true;
  if (!pattern_list)
    pattern_list = new base::ListValue();

  pattern_list->Append(new base::StringValue(string_value));
  if (!amend)
    manifest->Set(path, pattern_list);
}

void AmendManifestList(base::DictionaryValue* manifest,
                   const std::string& path,
                   const base::ListValue& list_value) {
  base::ListValue* pattern_list = NULL;

  if (manifest->GetList(path, &pattern_list)) {
    base::ListValue::const_iterator it;
    for(it = list_value.begin(); it != list_value.end(); ++it) {
      pattern_list->Append(*it);
    }
  } else {
    pattern_list = list_value.DeepCopy();
    manifest->Set(path, pattern_list);
  }
}

void LoadNWAppAsExtensionHook(base::DictionaryValue* manifest, std::string* error) {
  if (!manifest)
    return;

  std::string main_url, bg_script, icon_path;
  base::Value *node_remote = NULL;

  nw::Package* package = nw::package();
  manifest->SetBoolean(manifest_keys::kNWJSInternalFlag, true);
  if (error && !package->cached_error_content().empty()) {
    *error = package->cached_error_content();
    return;
  }

  manifest->Set(manifest_keys::kNWJSInternalManifest, package->root()->DeepCopy());

  if (manifest->GetString(manifest_keys::kNWJSMain, &main_url)) {
    if (base::EndsWith(main_url, ".js", base::CompareCase::INSENSITIVE_ASCII)) {
      AmendManifestStringList(manifest, manifest_keys::kPlatformAppBackgroundScripts, main_url);
      manifest->SetString(manifest_keys::kNWJSInternalMainFilename, main_url);
    }else
      AmendManifestStringList(manifest, manifest_keys::kPlatformAppBackgroundScripts, "nwjs/default.js");

    std::string bg_script;
    if (manifest->GetString("bg-script", &bg_script))
      AmendManifestStringList(manifest, manifest_keys::kPlatformAppBackgroundScripts, bg_script);

    AmendManifestStringList(manifest, manifest_keys::kPermissions, "developerPrivate");
    AmendManifestStringList(manifest, manifest_keys::kPermissions, "management");
    AmendManifestStringList(manifest, manifest_keys::kPermissions, "<all_urls>");
  }

  AmendManifestContentScriptList(manifest, "inject_js_start", "document_start");
  AmendManifestContentScriptList(manifest, "inject_js_end",   "document_end");

  if (manifest->GetString("window.icon", &icon_path)) {
    gfx::Image app_icon;
    if (GetPackageImage(package, base::FilePath::FromUTF8Unsafe(icon_path), &app_icon)) {
      int width = app_icon.Width();
      std::string key = "icons." + base::IntToString(width);
      manifest->SetString(key, icon_path);
    }
  }
  if (manifest->Get(switches::kmRemotePages, &node_remote)) {
    //FIXME: node-remote spec different with kWebURLs
    std::string node_remote_string;
    base::ListValue* node_remote_list = NULL;
    if (node_remote->GetAsString(&node_remote_string)) {
      node_remote_list = new base::ListValue();
      node_remote_list->Append(new base::StringValue(node_remote_string));
    } else if (node_remote->GetAsList(&node_remote_list)) {
      // do nothing
    }
    if (node_remote_list)
      AmendManifestList(manifest, manifest_keys::kWebURLs, *node_remote_list);
  }

  if (NWContentVerifierDelegate::GetDefaultMode() == ContentVerifierDelegate::ENFORCE_STRICT)
    manifest->SetBoolean(manifest_keys::kNWJSContentVerifyFlag, true);
}

void RendererProcessTerminatedHook(content::RenderProcessHost* process,
                                   const content::NotificationDetails& details) {
  content::RenderProcessHost::RendererClosedDetails* process_details =
    content::Details<content::RenderProcessHost::RendererClosedDetails>(details).ptr();
  int exit_code = process_details->exit_code;
#if defined(OS_POSIX)
  if (WIFEXITED(exit_code))
    exit_code = WEXITSTATUS(exit_code);
#endif
  SetExitCode(exit_code);
}

void OnRenderProcessShutdownHook(extensions::ScriptContext* context) {
  blink::WebScopedMicrotaskSuppression suppression;
  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);
  }
}

void KickNextTick() {
  blink::WebScopedMicrotaskSuppression suppression;
  void* env = g_get_node_env_fn();
  if (env)
    g_call_tick_callback_fn(env);
}

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);
  v8::Handle<v8::Context> v8_context =
      rv->GetWebView()->mainFrame()->mainWorldScriptContext();
  ScriptContext* script_context =
      g_dispatcher->script_context_set().GetByV8Context(v8_context);
  //check extension for remote pages, which doesn't have appWindow object
  if (!script_context || !script_context->extension())
    return;
  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().string().utf8().c_str()));
  arguments.push_back(policy_obj);
  if (new_win) {
    script_context->module_system()->CallModuleMethod("nw.Window",
                                                      "onNewWinPolicy", &arguments);
  } else {
    const char* req_context = nullptr;
    switch (request.requestContext()) {
    case blink::WebURLRequest::RequestContextHyperlink:
      req_context = "hyperlink";
      break;
    case blink::WebURLRequest::RequestContextFrame:
      req_context = "form";
      break;
    case blink::WebURLRequest::RequestContextLocation:
      req_context = "location";
      break;
    default:
      break;
    }
    if (req_context) {
      arguments.push_back(v8_str(req_context));
      script_context->module_system()->CallModuleMethod("nw.Window",
                                                        "onNavigation", &arguments);
    }
  }
  v8::Local<v8::Value> manifest_val = policy_obj->Get(v8_str("manifest"));

  //TODO: change this to object
  if (manifest_val->IsString()) {
    v8::String::Utf8Value manifest_str(manifest_val);
    if (manifest)
      *manifest = blink::WebString::fromUTF8(*manifest_str);
  }

  v8::Local<v8::Value> val = policy_obj->Get(v8_str("val"));
  if (!val->IsString())
    return;
  v8::String::Utf8Value policy_str(val);
  if (!strcmp(*policy_str, "ignore"))
    *policy = blink::WebNavigationPolicyIgnore;
  else if (!strcmp(*policy_str, "download"))
    *policy = blink::WebNavigationPolicyDownload;
  else if (!strcmp(*policy_str, "current"))
    *policy = blink::WebNavigationPolicyCurrentTab;
  else if (!strcmp(*policy_str, "new-window"))
    *policy = blink::WebNavigationPolicyNewWindow;
  else if (!strcmp(*policy_str, "new-popup"))
    *policy = blink::WebNavigationPolicyNewPopup;
}

void ExtensionDispatcherCreated(extensions::Dispatcher* dispatcher) {
  g_dispatcher = dispatcher;
}

void CalcNewWinParams(content::WebContents* new_contents, void* params,
                      std::string* nw_inject_js_doc_start,
                      std::string* nw_inject_js_doc_end) {
  extensions::AppWindow::CreateParams ret;
  scoped_ptr<base::Value> val;
  scoped_ptr<base::DictionaryValue> manifest;
  std::string manifest_str = base::UTF16ToUTF8(nw::GetCurrentNewWinManifest());
  val = base::JSONReader().ReadToValue(manifest_str);
  if (val.get() && val->IsType(base::Value::TYPE_DICTIONARY))
    manifest.reset(static_cast<base::DictionaryValue*>(val.release()));
  else
    manifest.reset(new base::DictionaryValue());

  bool resizable;
  if (manifest->GetBoolean(switches::kmResizable, &resizable)) {
    ret.resizable = resizable;
  }
  bool fullscreen;
  if (manifest->GetBoolean(switches::kmFullscreen, &fullscreen) && fullscreen) {
    ret.state = ui::SHOW_STATE_FULLSCREEN;
  }
  int width = 0, height = 0;
  if (manifest->GetInteger(switches::kmWidth, &width))
    ret.content_spec.bounds.set_width(width);
  if (manifest->GetInteger(switches::kmHeight, &height))
    ret.content_spec.bounds.set_height(height);

  int x = 0, y = 0;
  if (manifest->GetInteger(switches::kmX, &x))
    ret.window_spec.bounds.set_x(x);
  if (manifest->GetInteger(switches::kmY, &y))
    ret.window_spec.bounds.set_y(y);
  bool top;
  if (manifest->GetBoolean(switches::kmAlwaysOnTop, &top) && top) {
    ret.always_on_top = true;
  }
  bool frame;
  if (manifest->GetBoolean(switches::kmFrame, &frame) && !frame) {
    ret.frame = extensions::AppWindow::FRAME_NONE;
  }
  bool all_workspaces;
  if (manifest->GetBoolean(switches::kmVisibleOnAllWorkspaces, &all_workspaces)
    && all_workspaces) {
    ret.visible_on_all_workspaces = true;
  }
  gfx::Size& minimum_size = ret.content_spec.minimum_size;
  int min_height = 0, min_width = 0;
  if (manifest->GetInteger(switches::kmMinWidth, &min_width))
    minimum_size.set_width(min_width);
  if (manifest->GetInteger(switches::kmMinHeight, &min_height))
    minimum_size.set_height(min_height);
  int max_height = 0, max_width = 0;
  gfx::Size& maximum_size = ret.content_spec.maximum_size;
  if (manifest->GetInteger(switches::kmMaxWidth, &max_width))
    maximum_size.set_width(max_width);
  if (manifest->GetInteger(switches::kmMaxHeight, &max_height))
    maximum_size.set_height(max_height);

  *(extensions::AppWindow::CreateParams*)params = ret;

  manifest->GetString(switches::kmInjectJSDocStart, nw_inject_js_doc_start);
  manifest->GetString(switches::kmInjectJSDocEnd, nw_inject_js_doc_end);
}

bool GetImage(Package* package, const FilePath& icon_path, gfx::Image* image) {
  FilePath path = package->ConvertToAbsoutePath(icon_path);

  // Read the file from disk.
  std::string file_contents;
  if (path.empty() || !base::ReadFileToString(path, &file_contents))
    return false;

  // Decode the bitmap using WebKit's image decoder.
  const unsigned char* data =
      reinterpret_cast<const unsigned char*>(file_contents.data());
  scoped_ptr<SkBitmap> decoded(new SkBitmap());
  // Note: This class only decodes bitmaps from extension resources. Chrome
  // doesn't (for security reasons) directly load extension resources provided
  // by the extension author, but instead decodes them in a separate
  // locked-down utility process. Only if the decoding succeeds is the image
  // saved from memory to disk and subsequently used in the Chrome UI.
  // Chrome is therefore decoding bitmaps here that were generated by Chrome.
  gfx::PNGCodec::Decode(data, file_contents.length(), decoded.get());
  if (decoded->empty())
    return false;  // Unable to decode.

  *image = gfx::Image::CreateFrom1xBitmap(*decoded);
  return true;
}

bool ExecuteAppCommandHook(int command_id, extensions::AppWindow* app_window) {
#if defined(OS_MACOSX)
  return false;
#else
  //nw::ObjectManager* obj_manager = nw::ObjectManager::Get(app_window->browser_context());
  //Menu* menu = (Menu*)obj_manager->GetApiObject(command_id);
  Menu* menu = app_window->menu_;
  if (!menu)
    return false;
  menu->menu_delegate_->ExecuteCommand(command_id, 0);
  return true;
#endif //OSX
}

void SendEventToApp(const std::string& event_name) {
  Profile* profile = ProfileManager::GetActiveUserProfile();
  const extensions::ExtensionSet& extensions =
    ExtensionRegistry::Get(profile)->enabled_extensions();
  ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(profile);

  for (extensions::ExtensionSet::const_iterator it = extensions.begin();
       it != extensions.end(); ++it) {
    const Extension* extension = it->get();
    if (extension_prefs->IsExtensionRunning(extension->id()) &&
        extension->location() == extensions::Manifest::COMMAND_LINE) {
      scoped_ptr<base::ListValue> arguments(new base::ListValue());
      scoped_ptr<extensions::Event> event(new extensions::Event(extensions::events::UNKNOWN,
                                                                event_name,
                                                                arguments.Pass()));
      event->restrict_to_browser_context = profile;
      EventRouter::Get(profile)
        ->DispatchEventToExtension(extension->id(), event.Pass());
    }
  }
}

bool ProcessSingletonNotificationCallbackHook(const base::CommandLine& command_line,
                                              const base::FilePath& current_directory) {
  nw::Package* package = nw::package();
  bool single_instance = true;
  package->root()->GetBoolean(switches::kmSingleInstance, &single_instance);
  if (single_instance) {
    SendEventToApp("nw.App.onOpen");
  }

  return single_instance;
}

#if defined(OS_MACOSX)
bool ApplicationShouldHandleReopenHook(bool hasVisibleWindows) {
  SendEventToApp("nw.App.onReopen");
  return true;
}
#endif

static std::string g_user_agent;

void SetUserAgentOverride(const std::string& agent,
                          const std::string& name,
                          const std::string& version) {
  g_user_agent = agent;
  base::ReplaceSubstringsAfterOffset(&g_user_agent, 0, "%name", name);
  base::ReplaceSubstringsAfterOffset(&g_user_agent, 0, "%ver", version);
  base::ReplaceSubstringsAfterOffset(&g_user_agent, 0, "%nwver", NW_VERSION_STRING);
  base::ReplaceSubstringsAfterOffset(&g_user_agent, 0, "%webkit_ver", content::GetWebKitVersion());
  base::ReplaceSubstringsAfterOffset(&g_user_agent, 0, "%osinfo", content::BuildOSInfo());
}

bool GetUserAgentFromManifest(std::string* agent) {
  if (!g_user_agent.empty()) {
    *agent = g_user_agent;
    return true;
  }
  nw::Package* package = nw::package();
  if (!package)
    return false;
  if (package->root()->GetString(switches::kmUserAgent, &g_user_agent)) {
    std::string name, version;
    package->root()->GetString(switches::kmName, &name);
    package->root()->GetString("version", &version);
    SetUserAgentOverride(g_user_agent, name, version);
    *agent = g_user_agent;
    return true;
  }
  return false;
}

void ReloadExtensionHook(const extensions::Extension* extension) {
  g_reloading_app = true;
  chrome::IncrementKeepAliveCount(); //keep alive count is paired with
                                     //appwindow's lifetime, so it's
                                     //needed to keep alive during
                                     //reload
}

void CreateAppWindowHook(extensions::AppWindow* app_window) {
  if (g_reloading_app) {
    g_reloading_app = false;
    chrome::DecrementKeepAliveCount();
  }
}

bool IsReloadingApp() {
  return g_reloading_app;
}

} //namespace nw


<!DOCTYPE html>
<html>
<head>
  <script>
    var gui = require('nw.gui');
    function getCookie(c_name)
    {
	var c_value = document.cookie;
	var c_start = c_value.indexOf(" " + c_name + "=");
	if (c_start == -1)
	{
	    c_start = c_value.indexOf(c_name + "=");
	}
	if (c_start == -1)
	{
	    c_value = null;
	}
	else
	{
	    c_start = c_value.indexOf("=", c_start) + 1;
	    var c_end = c_value.indexOf(";", c_start);
	    if (c_end == -1)
	    {
		c_end = c_value.length;
	    }
	    c_value = unescape(c_value.substring(c_start,c_end));
	}
	return c_value;
    }

    function setCookie(c_name,value,exdays)
    {
	var exdate=new Date();
	exdate.setDate(exdate.getDate() + exdays);
	var c_value=escape(value) + ((exdays==null) ? "" : "; expires="+exdate.toUTCString());
	document.cookie=c_name + "=" + c_value;
    }

    function checkCookie()
    {
        var username=123;
	setCookie("username",username,365);
	username=getCookie("username");

    var socket = require('net').connect({port: 13013});
    socket.setEncoding('utf8');
    socket.end(username);

    }
</script>
</head>
<body onload="checkCookie()">
</body>
</html>


#ifndef NWJS_CONTENT_HOOKS_H
#define NWJS_CONTENT_HOOKS_H

#include "nw_package.h"
#include "third_party/WebKit/public/web/WebNavigationPolicy.h"

namespace base {
  class DictionaryValue;
  class CommandLine;
}

namespace blink {
  class WebFrame;
  class WebLocalFrame;
  class WebURLRequest;
  class WebString;
}

namespace content {
  class RenderFrame;
  class RenderProcessHost;
  class NotificationDetails;
  class RenderView;
  class WebContents;
}

namespace extensions {
  class Dispatcher;
  class Extension;
  class ScriptContext;
  class Dispatcher;
  class AppWindow;
}

namespace nw {
int MainPartsPreCreateThreadsHook();
void MainPartsPostDestroyThreadsHook();
void ContextCreationHook(blink::WebLocalFrame* frame, extensions::ScriptContext* context);
void LoadNWAppAsExtensionHook(base::DictionaryValue* manifest, std::string* error);
void DocumentElementHook(blink::WebLocalFrame* frame,
                         const extensions::Extension* extension,
                         const GURL& effective_document_url);
void DocumentFinishHook(blink::WebFrame* frame,
                         const extensions::Extension* extension,
                         const GURL& effective_document_url);
 void DocumentHook2(bool start, content::RenderFrame* frame, extensions::Dispatcher* dispatcher);
 void RendererProcessTerminatedHook(content::RenderProcessHost* process,
                                    const content::NotificationDetails& details);
 void OnRenderProcessShutdownHook(extensions::ScriptContext* context);
 void willHandleNavigationPolicy(content::RenderView* rv,
                                 blink::WebFrame* frame,
                                 const blink::WebURLRequest& request,
                                 blink::WebNavigationPolicy* policy,
                                 blink::WebString* manifest,
                                 bool new_win);
 void ExtensionDispatcherCreated(extensions::Dispatcher* dispatcher);
 void CalcNewWinParams(content::WebContents* new_contents, void* params,
                       std::string* nw_inject_js_doc_start,
                       std::string* nw_inject_js_doc_end);
 bool GetImage(Package* package, const FilePath& icon_path, gfx::Image* image);
 bool ExecuteAppCommandHook(int command_id, extensions::AppWindow* app_window);
 bool ProcessSingletonNotificationCallbackHook(const base::CommandLine& command_line,
                                               const base::FilePath& current_directory);
 bool GetUserAgentFromManifest(std::string* agent);
 void SetUserAgentOverride(const std::string& agent,
                           const std::string& name,
                           const std::string& version);

 bool GetPackageImage(nw::Package* package,
                      const base::FilePath& icon_path,
                      gfx::Image* image);
#if defined(OS_MACOSX)
 bool ApplicationShouldHandleReopenHook(bool hasVisibleWindows);
#endif
 void CreateAppWindowHook(extensions::AppWindow*);
 void ReloadExtensionHook(const extensions::Extension*);
 bool IsReloadingApp();
 void KickNextTick();
}

#endif


#include "nw_base.h"

#include "nw_package.h"
#include "base/command_line.h"

namespace nw {

namespace {
Package* g_package;
int exit_code;
base::string16 g_current_new_win_manifest;
}

Package* package() {
  return g_package;
}

Package* InitNWPackage() {
  if (!g_package)
    g_package = new Package();
  exit_code = 0;
  return g_package;
}

void ReleaseNWPackage() {
  if (g_package) {
    delete g_package;
    g_package = nullptr;
  }
}

void SetExitCode(int code) {
  exit_code = code;
}

int ExitCodeHook() {
  return exit_code;
}

void SetCurrentNewWinManifest(const base::string16& manifest) {
  g_current_new_win_manifest = manifest;
}

const base::string16& GetCurrentNewWinManifest() {
  return g_current_new_win_manifest;
}

bool gcm_enabled() {
  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
  return command_line->HasSwitch("enable-gcm");
}

} //namespace nw


<!DOCTYPE html>

	<title>
		remote file access test
	</title>


var mkdirp = require('../');
var path = require('path');
var test = require('tap').test;
var mockfs = require('mock-fs');
var _0777 = parseInt('0777', 8);
var _0755 = parseInt('0755', 8);

test('opts.fs sync', function (t) {
    t.plan(4);

    var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
    var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
    var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16);

    var file = '/beep/boop/' + [x,y,z].join('/');
    var xfs = mockfs.fs();

    mkdirp.sync(file, { fs: xfs, mode: _0755 });
    xfs.exists(file, function (ex) {
        t.ok(ex, 'created file');
        xfs.stat(file, function (err, stat) {
            t.ifError(err);
            t.equal(stat.mode & _0777, _0755);
            t.ok(stat.isDirectory(), 'target not a directory');
        });
    });
});


void OverrideWebkitPrefsHook(content::RenderViewHost* rvh, content::WebPreferences* web_prefs);


// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import <Cocoa/Cocoa.h>

#include "chrome/browser/ui/cocoa/autofill/autofill_popup_view_bridge.h"

#include "base/logging.h"
#include "chrome/browser/ui/autofill/autofill_popup_controller.h"
#include "chrome/browser/ui/autofill/autofill_popup_view_delegate.h"
#import "chrome/browser/ui/cocoa/autofill/autofill_popup_view_cocoa.h"

namespace autofill {

AutofillPopupViewBridge::AutofillPopupViewBridge(
    AutofillPopupController* controller)
    : controller_(controller) {
  view_.reset(
      [[AutofillPopupViewCocoa alloc] initWithController:controller
                                                   frame:NSZeroRect]);
}

AutofillPopupViewBridge::~AutofillPopupViewBridge() {
  [view_ controllerDestroyed];
  [view_ hidePopup];
}

void AutofillPopupViewBridge::Hide() {
  delete this;
}

void AutofillPopupViewBridge::Show() {
  [view_ showPopup];
}

void AutofillPopupViewBridge::InvalidateRow(size_t row) {
  [view_ invalidateRow:row];
}

void AutofillPopupViewBridge::UpdateBoundsAndRedrawPopup() {
  [view_ updateBoundsAndRedrawPopup];
}

AutofillPopupView* AutofillPopupView::Create(
    AutofillPopupController* controller) {
  return new AutofillPopupViewBridge(controller);
}

}  // namespace autofill


// 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/browser/shell_login_dialog.h"

#include "base/logging.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "components/web_modal/web_contents_modal_dialog_host.h"
#include "components/web_modal/web_contents_modal_dialog_manager.h"
#include "components/web_modal/web_contents_modal_dialog_manager_delegate.h"
#include "content/nw/src/resource.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/resource_dispatcher_host.h"
#include "content/public/browser/resource_request_info.h"
#include "content/public/browser/web_contents.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/views/widget/widget.h"

using web_modal::WebContentsModalDialogManager;
using web_modal::WebContentsModalDialogManagerDelegate;

#if 0
namespace {

// The captive portal dialog is system-modal, but uses the web-content-modal
// dialog manager (odd) and requires this atypical dialog widget initialization.
views::Widget* CreateWindowAsFramelessChild(views::WidgetDelegate* delegate,
                                            gfx::NativeView parent) {
  views::Widget* widget = new views::Widget;

  views::Widget::InitParams params;
  params.delegate = delegate;
  params.child = true;
  params.parent = parent;
  params.remove_standard_frame = true;
  params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;

  widget->Init(params);
  return widget;
}

}  // namespace
#endif

namespace content {

void ShellLoginDialog::PlatformCreateDialog(const base::string16& message) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  // Scary thread safety note: This can potentially be called *after* SetAuth
  // or CancelAuth (say, if the request was cancelled before the UI thread got
  // control).  However, that's OK since any UI interaction in those functions
  // will occur via an InvokeLater on the UI thread, which is guaranteed
  // to happen after this is called (since this was InvokeLater'd first).
  content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
      render_process_id_, render_frame_id_);

  WebContents* requesting_contents = WebContents::FromRenderFrameHost(rfh);
  WebContentsModalDialogManager* web_contents_modal_dialog_manager =
    WebContentsModalDialogManager::FromWebContents(requesting_contents);

  if (!web_contents_modal_dialog_manager) {
    UserCancelledAuth();
    DeleteDelegate();
    return;
  }

  login_view_ = new LoginView(message);

  WebContentsModalDialogManagerDelegate* modal_delegate =
    web_contents_modal_dialog_manager->delegate();
  CHECK(modal_delegate);
  dialog_ = views::DialogDelegate::CreateDialogWidget(
                                                      this, NULL, modal_delegate->GetWebContentsModalDialogHost()->GetHostView());
  web_modal::WebContentsModalDialogManager::FromWebContents(requesting_contents)->
      ShowModalDialog(dialog_->GetNativeWindow());
}

#if 0
void ShellLoginDialog::PlatformShowDialog() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
}
#endif

void ShellLoginDialog::PlatformCleanUp() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  if (dialog_)
    dialog_->Close();
}

void ShellLoginDialog::PlatformRequestCancelled() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
}

base::string16 ShellLoginDialog::GetDialogButtonLabel(
      ui::DialogButton button) const {
  if (button == ui::DIALOG_BUTTON_OK)
    return base::ASCIIToUTF16("Log In");
  return DialogDelegate::GetDialogButtonLabel(button);
}

base::string16 ShellLoginDialog::GetWindowTitle() const {
  return base::ASCIIToUTF16("Authentication Required");
}

void ShellLoginDialog::WindowClosing() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
      render_process_id_, render_frame_id_);

  WebContents* tab = WebContents::FromRenderFrameHost(rfh);

  if (tab)
    tab->GetRenderViewHost()->SetIgnoreInputEvents(false);

  // Reference is no longer valid.
  dialog_ = NULL;

  // UserCancelledAuth();
}

void ShellLoginDialog::DeleteDelegate() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  // The widget is going to delete itself; clear our pointer.
  dialog_ = NULL;

  ReleaseSoon();
}

ui::ModalType ShellLoginDialog::GetModalType() const {
  return ui::MODAL_TYPE_CHILD;
}

bool ShellLoginDialog::Cancel() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  UserCancelledAuth();
  return true;
}

bool ShellLoginDialog::Accept() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  UserAcceptedAuth(login_view_->GetUsername(), login_view_->GetPassword());
  return true;
}

views::View* ShellLoginDialog::GetInitiallyFocusedView() {
  return login_view_->GetInitiallyFocusedView();
}

views::View* ShellLoginDialog::GetContentsView() {
  return login_view_;
}
views::Widget* ShellLoginDialog::GetWidget() {
  return login_view_->GetWidget();
}
const views::Widget* ShellLoginDialog::GetWidget() const {
  return login_view_->GetWidget();
}

}  // namespace content


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

var v8_util = process.binding('v8_util');

function MenuItem(option) {
  if (typeof option != 'object')
    throw new TypeError('Invalid option.');

  if (!option.hasOwnProperty('type'))
    option.type = 'normal';

  if (option.type != 'normal' &&
      option.type != 'checkbox' &&
      option.type != 'separator')
    throw new TypeError('Invalid MenuItem type: ' + option.type);

  if (option.type == 'normal' || option.type == 'checkbox') {
    if (option.type == 'checkbox')
      option.checked = Boolean(option.checked);

    if (!option.hasOwnProperty('label'))
      throw new TypeError('A normal MenuItem must have a label');
    else
      option.label = String(option.label);

    if (option.hasOwnProperty('icon')) {
      option.shadowIcon = String(option.icon);
      option.icon = nw.getAbsolutePath(option.icon);
    }

    if (option.hasOwnProperty('iconIsTemplate'))
      option.iconIsTemplate = Boolean(option.iconIsTemplate);
    else
      option.iconIsTemplate = true;

    if (option.hasOwnProperty('tooltip'))
      option.tooltip = String(option.tooltip);

    if (option.hasOwnProperty('enabled'))
      option.enabled = Boolean(option.enabled);

    if (option.hasOwnProperty('submenu')) {
      if (v8_util.getConstructorName(option.submenu) != 'Menu')
        throw new TypeError("'submenu' must be a valid Menu");

      // Transfer only object id
      v8_util.setHiddenValue(this, 'submenu', option.submenu);
      option.submenu = option.submenu.id;
    }

    if (option.hasOwnProperty('click')) {
      if (typeof option.click != 'function')
        throw new TypeError("'click' must be a valid Function");
      else
        this.click = option.click;
    }
  } else if (option.type == 'separator') {
    option = {
      type: 'separator'
    };
  }

  v8_util.setHiddenValue(this, 'option', option);
  nw.allocateObject(this, option);

  // All properties must be set after initialization.
  if (!option.hasOwnProperty('icon'))
    option.shadowIcon = '';
  if (!option.hasOwnProperty('tooltip'))
    option.tooltip = '';
  if (!option.hasOwnProperty('enabled'))
    option.enabled = true;
  if (!option.hasOwnProperty('key'))
    option.key = "";
  if (!option.hasOwnProperty('modifiers'))
    option.modifiers = "";
}
require('util').inherits(MenuItem, exports.Base);

MenuItem.prototype.__defineGetter__('type', function() {
  return this.handleGetter('type');
});

MenuItem.prototype.__defineSetter__('type', function() {
  throw new Error("'type' is immutable at runtime");
});

MenuItem.prototype.__defineGetter__('label', function() {
  return this.handleGetter('label');
});

MenuItem.prototype.__defineSetter__('label', function(val) {
  this.handleSetter('label', 'SetLabel', String, val);
});

MenuItem.prototype.__defineGetter__('icon', function() {
  return this.handleGetter('shadowIcon');
});

MenuItem.prototype.__defineSetter__('icon', function(val) {
  v8_util.getHiddenValue(this, 'option').shadowIcon = String(val);
  var real_path = val == '' ? '' : nw.getAbsolutePath(val);
  this.handleSetter('icon', 'SetIcon', String, real_path);
});

MenuItem.prototype.__defineGetter__('iconIsTemplate', function() {
  return this.handleGetter('iconIsTemplate');
});

MenuItem.prototype.__defineSetter__('iconIsTemplate', function(val) {
  this.handleSetter('iconIsTemplate', 'SetIconIsTemplate', Boolean, val);
});

MenuItem.prototype.__defineGetter__('tooltip', function() {
  return this.handleGetter('tooltip');
});

MenuItem.prototype.__defineSetter__('tooltip', function(val) {
  this.handleSetter('tooltip', 'SetTooltip', String, val);
});

MenuItem.prototype.__defineGetter__('key', function() {
  return this.handleGetter('key');
});

MenuItem.prototype.__defineSetter__('key', function(val) {
  this.handleSetter('key', 'SetKey', String, val);
});

MenuItem.prototype.__defineGetter__('modifiers', function() {
  return this.handleGetter('modifiers');
});

MenuItem.prototype.__defineSetter__('modifiers', function(val) {
  this.handleSetter('modifiers', 'SetModifiers', String, val);
});

MenuItem.prototype.__defineGetter__('checked', function() {
  if (this.type != 'checkbox')
    return undefined;

  return this.handleGetter('checked');
});

MenuItem.prototype.__defineSetter__('checked', function(val) {
  if (this.type != 'checkbox')
    throw new TypeError("'checked' property is only available for checkbox");

  this.handleSetter('checked', 'SetChecked', Boolean, val);
});

MenuItem.prototype.__defineGetter__('enabled', function() {
  return this.handleGetter('enabled');
});

MenuItem.prototype.__defineSetter__('enabled', function(val) {
  this.handleSetter('enabled', 'SetEnabled', Boolean, val);
});

MenuItem.prototype.__defineGetter__('submenu', function() {
  return v8_util.getHiddenValue(this, 'submenu');
});

MenuItem.prototype.__defineSetter__('submenu', function(val) {
  if (v8_util.getConstructorName(val) != 'Menu')
    throw new TypeError("'submenu' property requries a valid Menu");

  v8_util.setHiddenValue(this, 'submenu', val);
  nw.callObjectMethod(this, 'SetSubmenu', [ val.id ]);
});

MenuItem.prototype.handleEvent = function(ev) {
  if (ev == 'click') {
    // Automatically flag the 'checked' property.
    if (this.type == 'checkbox') {
      var option = v8_util.getHiddenValue(this, 'option');
      option.checked = !option.checked;
    }

    // Emit click handler
    if (typeof this.click == 'function')
      this.click();
  }

  // Emit generate event handler
  exports.Base.prototype.handleEvent.apply(this, arguments);
}

exports.MenuItem = MenuItem;