  <title>jsparse Docs</title>

    {{> page}}

<template name="page">

This is the spec for the JavaScript syntax tree returned by jsparse.

It's notable that every token from the source code appears in order in this syntax tree, which
   is a Concrete Syntax Tree (CST) rather than an abstract AST.

  {{spacer}}

The tree consists of nodes
    and tokens.  A node consists of a name followed
    by zero or more child nodes or tokens.  A token consists of
    one or more characters from the source code.

For example, the following tree, which might have come from parsing 1 + 2,
    contains nodes named "binary",
    "number", and "number"
    and the tokens 1, +,
    and 2:

{{#nodespec}}["binary", ["number", `1`], `+`, ["number", `2`]]{{/nodespec}}

  {{spacer}}

The following notation is used throughout this document to give schematic definitions of
      the different types of nodes.

An italicized word refers to a list of one or more possible
      nodes or tokens that could fill a certain spot.  For example, the following is the
      definition of the "binary" node:

{{#nodespec}}["binary", expression, binaryOp, expression]{{/nodespec}}

The expression spot could be filled by one of over 20 types
      of expression nodes, and if you look up binaryOp you'll see
      it refers to any of a list of binary operator tokens.

A vertical bar (|) separates alternatives, of which exactly
      one must be present in the tree.  An ellipsis (...) stands
      for a sequence of zero or more of the preceding item.  For example:

    {{#nodespec}}["program", (statement | functionDecl), ...]{{/nodespec}}

This definition says that a program node contains zero or more items, each of which may be either a
      statement or a functionDecl.

A question mark (?) indicates that the previous item
      (or items enclosed in parentheses) may or may not present.

Token classes like IDENTIFIER match any token in the class.  The BOOLEAN, NUMBER, STRING, and REGEX classes are all literals.  An example STRING would be "hello".

      {{spacer}}

Some definitions with ellipses may seem strangely permissive, such as:

  {{#nodespec}}["varStmnt", `var`, (varDecl | `,`), ..., semi]{{/nodespec}}

This seems to say that var; and var,,x, are valid statements.
       The reason for this style of definition is to discourage reliance on exact comma
       positions, which may occasionally vary between JavaScript implementations and versions.
       For example, the 5th edition of ECMAScript allows a trailing comma in object literals whereas
       the 3rd edition doesn't.

For most applications, the commas aren't that important, and you can read all the varDecls
       by just skipping any commas you encounter.

{{#nodespec}}program:{{/nodespec}}

  {{#nodespec}}["program", (statement | functionDecl), ...]{{/nodespec}}

{{#nodespec}}statement:{{/nodespec}}

  {{#nodespec}}["expressionStmnt", expression, semi]{{/nodespec}}

Function calls and assignments are expressions.

  {{#nodespec}}["emptyStmnt", `;`]{{/nodespec}}

Only an actual ; token can create an empty statement.

  {{#nodespec}}["blockStmnt", `{`, statement, ..., `}`]{{/nodespec}}
  {{#nodespec}}["varStmnt", `var`, (varDecl | `,`), ..., semi]{{/nodespec}}

You can assume at least one varDecl is present.

  {{#nodespec}}["ifStmnt", `if`, `(`, expression, `)`, statement, (`else`, statement)?]{{/nodespec}}
  {{#nodespec}}["whileStmnt", `while`, `(`, expression, `)`, statement]{{/nodespec}}
  {{#nodespec}}["doStmnt", `do`, statement, `while`, `(`, expression, `)`, semi]{{/nodespec}}
  {{#nodespec}}["forStmnt", `for`, `(`, forSpec, `)`, statement]{{/nodespec}}
  {{#nodespec}}["returnStmnt", `return`, (expression | nil), semi]{{/nodespec}}
  {{#nodespec}}["continueStmnt", `continue`, (IDENTIFIER | nil), semi]{{/nodespec}}
  {{#nodespec}}["breakStmnt", `break`, (IDENTIFIER | nil), semi]{{/nodespec}}
  {{#nodespec}}["throwStmnt", `throw`, expression, semi]{{/nodespec}}
  {{#nodespec}}["withStmnt", `with`, `(`, expression, `)`, statement]{{/nodespec}}
  {{#nodespec}}["switchStmnt", `switch`, `(`, expression, `)`, `{`, (case | default), ..., `}`]{{/nodespec}}

There's at most one default clause, but it can be anywhere in the list of cases and defaults.

  {{#nodespec}}["tryStmnt", `try`, statement, (catch | nil), (finally | nil)]{{/nodespec}}

The statement is always a blockStmnt.

  {{#nodespec}}["labelStmnt", IDENTIFIER, `:`, statement]{{/nodespec}}
  {{#nodespec}}["debuggerStmnt", `debugger`, semi]{{/nodespec}}

{{#nodespec}}functionDecl:{{/nodespec}}

  {{#nodespec}}["functionDecl", `function`, IDENTIFIER, `(`, (IDENTIFIER | `,`), ..., `)`, `{`, (statement | functionDecl), ..., `}`]{{/nodespec}}

Different from a functionExpr only in that the function name is required and not optional.

{{#nodespec}}expression:{{/nodespec}}

{{#nodespec}}["this", `this`]{{/nodespec}}
{{#nodespec}}["null", `null`]{{/nodespec}}
{{#nodespec}}["number", NUMBER]{{/nodespec}}
{{#nodespec}}["boolean", BOOLEAN]{{/nodespec}}
{{#nodespec}}["regex", REGEX]{{/nodespec}}
{{#nodespec}}["string", STRING]{{/nodespec}}
{{#nodespec}}["identifier", IDENTIFIER]{{/nodespec}}
{{#nodespec}}["parens", `(`, expression, `)`]{{/nodespec}}
{{#nodespec}}["array", `[`, (expression | `,`), ..., `]`]{{/nodespec}}

All commas are significant because of element elision, and any
      combination of commas and expressions is possible.
    The array [,,,7,,8,,] has 7 and 8 as
    its 3rd and 5th elements.

{{#nodespec}}["object", `{`, (prop | `,`), ..., `}`]{{/nodespec}}
{{#nodespec}}["functionExpr", `function`, (IDENTIFIER | nil), `(`, (IDENTIFIER | `,`), ..., `)`, `{`, (statement | functionDecl), ..., `}`]{{/nodespec}}
{{#nodespec}}["dot", expression, `.`, identifierName]{{/nodespec}}
{{#nodespec}}["bracket", expression, `[`, expression, `]`]{{/nodespec}}
{{#nodespec}}["call", expression, `(`, (expression | `,`), ...,  `)`]{{/nodespec}}
{{#nodespec}}["new", `new`, expression]{{/nodespec}}
{{#nodespec}}["newcall", `new`, expression, `(`, (expression | `,`), ...,  `)`]{{/nodespec}}
{{#nodespec}}["unary", unaryOp, expression]{{/nodespec}}
{{#nodespec}}["binary", expression, binaryOp, expression]{{/nodespec}}
{{#nodespec}}["postfix", expression, postfixOp]{{/nodespec}}
{{#nodespec}}["ternary", expression, `?`, expression, `:`, expression]{{/nodespec}}
{{#nodespec}}["assignment", expression, assignmentOp, expression]{{/nodespec}}

There is no simple constraint on what the first expression can be.
  A left-hand-side expression could be a simple identifier like foo,
or it could be any expression ending in a property access, such as foo().bar[baz].
Parentheses are also allowed around the left-hand side, so surprisingly ((x)) = 3
is completely legal and equivalent to x = 3.  This is because
JavaScript evaluates the left-hand side all the way down to the final variable reference.

{{#nodespec}}["comma", (expression | `,`), ...]{{/nodespec}}

You can assume there are at least two expressions, since there would have been no comma otherwise.

{{#nodespec}}nil:{{/nodespec}}

  {{#nodespec}}["nil"]{{/nodespec}}

Serves as a placeholder for optional parts of nodes.

{{#nodespec}}semi:{{/nodespec}}

  {{#nodespec}}[";"] | `;`{{/nodespec}}

An optional semicolon at the end of a statement.  Most semicolons in JavaScript can legally
    be omitted if a line break follows.  If the semicolon was omitted, a ";"
    node takes its place.

{{#nodespec}}prop:{{/nodespec}}

  {{#nodespec}}["prop", propertyName, `:`, expression]{{/nodespec}}

{{#nodespec}}propertyName:{{/nodespec}}

  {{#nodespec}}["idPropName", identifierName]{{/nodespec}}
  {{#nodespec}}["strPropName", STRING]{{/nodespec}}
  {{#nodespec}}["numPropName", NUMBER]{{/nodespec}}

A property name in an object literal can be given as a bare identifier, a quoted string literal,
    or a number literal.  These nodes indicate which it is.

{{#nodespec}}varDecl:{{/nodespec}}

  {{#nodespec}}["varDecl", IDENTIFIER, (`=`, expression)?]{{/nodespec}}

{{#nodespec}}forSpec:{{/nodespec}}

  {{#nodespec}}["forSpec", (expression | nil), `;`, (expression | nil), `;`, (expression | nil)]{{/nodespec}}
  {{#nodespec}}["forVarSpec", `var`, (varDecl | `,`), ..., `;`, (expression | nil), `;`, (expression | nil)]{{/nodespec}}
  {{#nodespec}}["forInSpec", expression, `in`, expression]{{/nodespec}}
  {{#nodespec}}["forVarInSpec", `var`, varDecl, `in`, expression]{{/nodespec}}

There are technically four types of for-loops in JavaScript.

These definitions suggest some
      odd possibilities, and interestingly, they work.  The form for (x.foo in y) will actually
      set x.foo each time through the loop, and for (var x = bar() in y) will call
      bar() and assign it first thing,
      even if x is immediately overwritten before the first iteration of the loop.

The semicolons are mandatory, even at a line break.

{{#nodespec}}case:{{/nodespec}}

  {{#nodespec}}["case", `case`, expression, `:`, statement, ...]{{/nodespec}}

{{#nodespec}}default:{{/nodespec}}

  {{#nodespec}}["default", `default`, `:`, statement, ...]{{/nodespec}}

{{#nodespec}}catch:{{/nodespec}}

  {{#nodespec}}["catch", `catch`, `(`, IDENTIFIER, `)`, statement]{{/nodespec}}

The statement is always a blockStmnt.

{{#nodespec}}finally:{{/nodespec}}

  {{#nodespec}}["finally", `finally`, statement]{{/nodespec}}

The statement is always a blockStmnt.

{{#nodespec}}identifierName:{{/nodespec}}

  {{#nodespec}}(IDENTIFIER | KEYWORD | BOOLEAN | `null`){{/nodespec}}

As of ECMAScript 5th edition, keywords and reserved words can be used in some places
      where an identifier is expected.  For example, x.return() or
      {true: 'yes'}.

{{#nodespec}}unaryOp:{{/nodespec}}

  {{#nodespec}}(`delete` | `void` | `typeof` | `++` | `--` | `+` | `-` | `~` | `!`){{/nodespec}}

{{#nodespec}}binaryOp:{{/nodespec}}

  {{#nodespec}}(`*` | `/` | `%` | `+` | `-` | `<<` | `>>` | `>>>` | `<` | `>` | `<=` | `>=` | `instanceof` | `in` | `==` | `!=` | `===` | `!==` | `&` | `^` | `|` | `&&` | `||`){{/nodespec}}

{{#nodespec}}postfixOp:{{/nodespec}}

  {{#nodespec}}(`++` | `--`){{/nodespec}}

{{#nodespec}}assignmentOp:{{/nodespec}}

  {{#nodespec}}(`=` | `*=` | `/=` | `%=` | `+=` | `-=` | `<<=` | `>>=` | `>>>=` | `&=` | `^=` | `|=`){{/nodespec}}

</template>


_.each(["client", "server"], function (sliceName) {

// standard client packages for the classic meteor stack.

// XXX remove and make everyone explicitly declare all dependencies

['meteor', 'webapp', 'deps', 'session', 'livedata', 'mongo-livedata',

var arch = sliceName === "server" ? "native" : "browser";

keep keep keep keep keep replace replace keep keep keep keep

['meteor', 'webapp', 'routepolicy', 'deps', 'session', 'livedata',

'mongo-livedata', 'spark', 'templating', 'startup', 'past', 'check'],

/**
 * History.js HTML4 Support
 * Depends on the HTML5 Support
 * @author Benjamin Arthur Lupton <contact@balupton.com>
 * @copyright 2010-2011 Benjamin Arthur Lupton <contact@balupton.com>
 * @license New BSD License <http://creativecommons.org/licenses/BSD/>
 */

(function(window,undefined){
	"use strict";

	// ========================================================================
	// Initialise

	// Localise Globals
	var
		document = window.document, // Make sure we are using the correct document
		setTimeout = window.setTimeout||setTimeout,
		clearTimeout = window.clearTimeout||clearTimeout,
		setInterval = window.setInterval||setInterval,
		History = window.History = window.History||{}; // Public History Object

	// Check Existence
	if ( typeof History.initHtml4 !== 'undefined' ) {
		throw new Error('History.js HTML4 Support has already been loaded...');
	}

	// ========================================================================
	// Initialise HTML4 Support

	// Initialise HTML4 Support
	History.initHtml4 = function(){
		// Initialise
		if ( typeof History.initHtml4.initialized !== 'undefined' ) {
			// Already Loaded
			return false;
		}
		else {
			History.initHtml4.initialized = true;
		}

		// ====================================================================
		// Properties

		/**
		 * History.enabled
		 * Is History enabled?
		 */
		History.enabled = true;

		// ====================================================================
		// Hash Storage

		/**
		 * History.savedHashes
		 * Store the hashes in an array
		 */
		History.savedHashes = [];

		/**
		 * History.isLastHash(newHash)
		 * Checks if the hash is the last hash
		 * @param {string} newHash
		 * @return {boolean} true
		 */
		History.isLastHash = function(newHash){
			// Prepare
			var oldHash = History.getHashByIndex(),
				isLast;

			// Check
			isLast = newHash === oldHash;

			// Return isLast
			return isLast;
		};

		/**
		 * History.saveHash(newHash)
		 * Push a Hash
		 * @param {string} newHash
		 * @return {boolean} true
		 */
		History.saveHash = function(newHash){
			// Check Hash
			if ( History.isLastHash(newHash) ) {
				return false;
			}

			// Push the Hash
			History.savedHashes.push(newHash);

			// Return true
			return true;
		};

		/**
		 * History.getHashByIndex()
		 * Gets a hash by the index
		 * @param {integer} index
		 * @return {string}
		 */
		History.getHashByIndex = function(index){
			// Prepare
			var hash = null;

			// Handle
			if ( typeof index === 'undefined' ) {
				// Get the last inserted
				hash = History.savedHashes[History.savedHashes.length-1];
			}
			else if ( index < 0 ) {
				// Get from the end
				hash = History.savedHashes[History.savedHashes.length+index];
			}
			else {
				// Get from the beginning
				hash = History.savedHashes[index];
			}

			// Return hash
			return hash;
		};

		// ====================================================================
		// Discarded States

		/**
		 * History.discardedHashes
		 * A hashed array of discarded hashes
		 */
		History.discardedHashes = {};

		/**
		 * History.discardedStates
		 * A hashed array of discarded states
		 */
		History.discardedStates = {};

		/**
		 * History.discardState(State)
		 * Discards the state by ignoring it through History
		 * @param {object} State
		 * @return {true}
		 */
		History.discardState = function(discardedState,forwardState,backState){
			//History.debug('History.discardState', arguments);
			// Prepare
			var discardedStateHash = History.getHashByState(discardedState),
				discardObject;

			// Create Discard Object
			discardObject = {
				'discardedState': discardedState,
				'backState': backState,
				'forwardState': forwardState
			};

			// Add to DiscardedStates
			History.discardedStates[discardedStateHash] = discardObject;

			// Return true
			return true;
		};

		/**
		 * History.discardHash(hash)
		 * Discards the hash by ignoring it through History
		 * @param {string} hash
		 * @return {true}
		 */
		History.discardHash = function(discardedHash,forwardState,backState){
			//History.debug('History.discardState', arguments);
			// Create Discard Object
			var discardObject = {
				'discardedHash': discardedHash,
				'backState': backState,
				'forwardState': forwardState
			};

			// Add to discardedHash
			History.discardedHashes[discardedHash] = discardObject;

			// Return true
			return true;
		};

		/**
		 * History.discardState(State)
		 * Checks to see if the state is discarded
		 * @param {object} State
		 * @return {bool}
		 */
		History.discardedState = function(State){
			// Prepare
			var StateHash = History.getHashByState(State),
				discarded;

			// Check
			discarded = History.discardedStates[StateHash]||false;

			// Return true
			return discarded;
		};

		/**
		 * History.discardedHash(hash)
		 * Checks to see if the state is discarded
		 * @param {string} State
		 * @return {bool}
		 */
		History.discardedHash = function(hash){
			// Check
			var discarded = History.discardedHashes[hash]||false;

			// Return true
			return discarded;
		};

		/**
		 * History.recycleState(State)
		 * Allows a discarded state to be used again
		 * @param {object} data
		 * @param {string} title
		 * @param {string} url
		 * @return {true}
		 */
		History.recycleState = function(State){
			//History.debug('History.recycleState', arguments);
			// Prepare
			var StateHash = History.getHashByState(State);

			// Remove from DiscardedStates
			if ( History.discardedState(State) ) {
				delete History.discardedStates[StateHash];
			}

			// Return true
			return true;
		};

		// ====================================================================
		// HTML4 HashChange Support

		if ( History.emulated.hashChange ) {
			/*
			 * We must emulate the HTML4 HashChange Support by manually checking for hash changes
			 */

			/**
			 * History.hashChangeInit()
			 * Init the HashChange Emulation
			 */
			History.hashChangeInit = function(){
				// Define our Checker Function
				History.checkerFunction = null;

				// Define some variables that will help in our checker function
				var lastDocumentHash = '',
					iframeId, iframe,
					lastIframeHash, checkerRunning;

				// Handle depending on the browser
				if ( History.isInternetExplorer() ) {
					// IE6 and IE7
					// We need to use an iframe to emulate the back and forward buttons

					// Create iFrame
					iframeId = 'historyjs-iframe';
					iframe = document.createElement('iframe');

					// Adjust iFarme
					iframe.setAttribute('id', iframeId);
					iframe.style.display = 'none';

					// Append iFrame
					document.body.appendChild(iframe);

					// Create initial history entry
					iframe.contentWindow.document.open();
					iframe.contentWindow.document.close();

					// Define some variables that will help in our checker function
					lastIframeHash = '';
					checkerRunning = false;

					// Define the checker function
					History.checkerFunction = function(){
						// Check Running
						if ( checkerRunning ) {
							return false;
						}

						// Update Running
						checkerRunning = true;

						// Fetch
						var documentHash = History.getHash()||'',
							iframeHash = History.unescapeHash(iframe.contentWindow.document.location.hash)||'';

						// The Document Hash has changed (application caused)
						if ( documentHash !== lastDocumentHash ) {
							// Equalise
							lastDocumentHash = documentHash;

							// Create a history entry in the iframe
							if ( iframeHash !== documentHash ) {
								//History.debug('hashchange.checker: iframe hash change', 'documentHash (new):', documentHash, 'iframeHash (old):', iframeHash);

								// Equalise
								lastIframeHash = iframeHash = documentHash;

								// Create History Entry
								iframe.contentWindow.document.open();
								iframe.contentWindow.document.close();

								// Update the iframe's hash
								iframe.contentWindow.document.location.hash = History.escapeHash(documentHash);
							}

							// Trigger Hashchange Event
							History.Adapter.trigger(window,'hashchange');
						}

						// The iFrame Hash has changed (back button caused)
						else if ( iframeHash !== lastIframeHash ) {
							//History.debug('hashchange.checker: iframe hash out of sync', 'iframeHash (new):', iframeHash, 'documentHash (old):', documentHash);

							// Equalise
							lastIframeHash = iframeHash;

							// Update the Hash
							History.setHash(iframeHash,false);
						}

						// Reset Running
						checkerRunning = false;

						// Return true
						return true;
					};
				}
				else {
					// We are not IE
					// Firefox 1 or 2, Opera

					// Define the checker function
					History.checkerFunction = function(){
						// Prepare
						var documentHash = History.getHash();

						// The Document Hash has changed (application caused)
						if ( documentHash !== lastDocumentHash ) {
							// Equalise
							lastDocumentHash = documentHash;

							// Trigger Hashchange Event
							History.Adapter.trigger(window,'hashchange');
						}

						// Return true
						return true;
					};
				}

				// Apply the checker function
				History.intervalList.push(setInterval(History.checkerFunction, History.options.hashChangeInterval));

				// Done
				return true;
			}; // History.hashChangeInit

			// Bind hashChangeInit
			History.Adapter.onDomLoad(History.hashChangeInit);

		} // History.emulated.hashChange

		// ====================================================================
		// HTML5 State Support

		// Non-Native pushState Implementation
		if ( History.emulated.pushState ) {
			/*
			 * We must emulate the HTML5 State Management by using HTML4 HashChange
			 */

			/**
			 * History.onHashChange(event)
			 * Trigger HTML5's window.onpopstate via HTML4 HashChange Support
			 */
			History.onHashChange = function(event){
				//History.debug('History.onHashChange', arguments);

				// Prepare
				var currentUrl = ((event && event.newURL) || document.location.href),
					currentHash = History.getHashByUrl(currentUrl),
					currentState = null,
					currentStateHash = null,
					currentStateHashExits = null,
					discardObject;

				// Check if we are the same state
				if ( History.isLastHash(currentHash) ) {
					// There has been no change (just the page's hash has finally propagated)
					//History.debug('History.onHashChange: no change');
					History.busy(false);
					return false;
				}

				// Reset the double check
				History.doubleCheckComplete();

				// Store our location for use in detecting back/forward direction
				History.saveHash(currentHash);

				// Expand Hash
				if ( currentHash && History.isTraditionalAnchor(currentHash) ) {
					//History.debug('History.onHashChange: traditional anchor', currentHash);
					// Traditional Anchor Hash
					History.Adapter.trigger(window,'anchorchange');
					History.busy(false);
					return false;
				}

				// Create State
				currentState = History.extractState(History.getFullUrl(currentHash||document.location.href,false),true);

				// Check if we are the same state
				if ( History.isLastSavedState(currentState) ) {
					//History.debug('History.onHashChange: no change');
					// There has been no change (just the page's hash has finally propagated)
					History.busy(false);
					return false;
				}

				// Create the state Hash
				currentStateHash = History.getHashByState(currentState);

				// Check if we are DiscardedState
				discardObject = History.discardedState(currentState);
				if ( discardObject ) {
					// Ignore this state as it has been discarded and go back to the state before it
					if ( History.getHashByIndex(-2) === History.getHashByState(discardObject.forwardState) ) {
						// We are going backwards
						//History.debug('History.onHashChange: go backwards');
						History.back(false);
					} else {
						// We are going forwards
						//History.debug('History.onHashChange: go forwards');
						History.forward(false);
					}
					return false;
				}

				// Push the new HTML5 State
				//History.debug('History.onHashChange: success hashchange');
				History.pushState(currentState.data,currentState.title,currentState.url,false);

				// End onHashChange closure
				return true;
			};
			History.Adapter.bind(window,'hashchange',History.onHashChange);

			/**
			 * History.pushState(data,title,url)
			 * Add a new State to the history object, become it, and trigger onpopstate
			 * We have to trigger for HTML4 compatibility
			 * @param {object} data
			 * @param {string} title
			 * @param {string} url
			 * @return {true}
			 */
			History.pushState = function(data,title,url,queue){
				//History.debug('History.pushState: called', arguments);

				// Check the State
				if ( History.getHashByUrl(url) ) {
					throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).');
				}

				// Handle Queueing
				if ( queue !== false && History.busy() ) {
					// Wait + Push to Queue
					//History.debug('History.pushState: we must wait', arguments);
					History.pushQueue({
						scope: History,
						callback: History.pushState,
						args: arguments,
						queue: queue
					});
					return false;
				}

				// Make Busy
				History.busy(true);

				// Fetch the State Object
				var newState = History.createStateObject(data,title,url),
					newStateHash = History.getHashByState(newState),
					oldState = History.getState(false),
					oldStateHash = History.getHashByState(oldState),
					html4Hash = History.getHash();

				// Store the newState
				History.storeState(newState);
				History.expectedStateId = newState.id;

				// Recycle the State
				History.recycleState(newState);

				// Force update of the title
				History.setTitle(newState);

				// Check if we are the same State
				if ( newStateHash === oldStateHash ) {
					//History.debug('History.pushState: no change', newStateHash);
					History.busy(false);
					return false;
				}

				// Update HTML4 Hash
				if ( newStateHash !== html4Hash && newStateHash !== History.getShortUrl(document.location.href) ) {
					//History.debug('History.pushState: update hash', newStateHash, html4Hash);
					History.setHash(newStateHash,false);
					return false;
				}

				// Update HTML5 State
				History.saveState(newState);

				// Fire HTML5 Event
				//History.debug('History.pushState: trigger popstate');
				History.Adapter.trigger(window,'statechange');
				History.busy(false);

				// End pushState closure
				return true;
			};

			/**
			 * History.replaceState(data,title,url)
			 * Replace the State and trigger onpopstate
			 * We have to trigger for HTML4 compatibility
			 * @param {object} data
			 * @param {string} title
			 * @param {string} url
			 * @return {true}
			 */
			History.replaceState = function(data,title,url,queue){
				//History.debug('History.replaceState: called', arguments);

				// Check the State
				if ( History.getHashByUrl(url) ) {
					throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).');
				}

				// Handle Queueing
				if ( queue !== false && History.busy() ) {
					// Wait + Push to Queue
					//History.debug('History.replaceState: we must wait', arguments);
					History.pushQueue({
						scope: History,
						callback: History.replaceState,
						args: arguments,
						queue: queue
					});
					return false;
				}

				// Make Busy
				History.busy(true);

				// Fetch the State Objects
				var newState        = History.createStateObject(data,title,url),
					oldState        = History.getState(false),
					previousState   = History.getStateByIndex(-2);

				// Discard Old State
				History.discardState(oldState,newState,previousState);

				// Alias to PushState
				History.pushState(newState.data,newState.title,newState.url,false);

				// End replaceState closure
				return true;
			};

		} // History.emulated.pushState

		// ====================================================================
		// Initialise

		// Non-Native pushState Implementation
		if ( History.emulated.pushState ) {
			/**
			 * Ensure initial state is handled correctly
			 */
			if ( History.getHash() && !History.emulated.hashChange ) {
				History.Adapter.onDomLoad(function(){
					History.Adapter.trigger(window,'hashchange');
				});
			}

		} // History.emulated.pushState

	}; // History.initHtml4

	// Try and Initialise History
	if ( typeof History.init !== 'undefined' ) {
		History.init();
	}

})(window);


keep keep keep add keep keep keep keep keep

#include "nw_content_browser_hooks.h"

// base
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/threading/thread_restrictions.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"

// content
#include "content/public/browser/child_process_security_policy.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
// #include "content/public/browser/plugin_service.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/renderer/v8_value_converter.h"
#include "content/public/common/web_preferences.h"
#include "content/public/browser/child_process_termination_info.h"

// content/nw
#include "content/nw/src/nw_base.h"
#include "content/nw/src/common/shell_switches.h"

#include "net/cert/x509_certificate.h"

// ui
#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"

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

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

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

using content::RenderProcessHost;

namespace content {
  class WebContents;
}

namespace nw {

namespace {

bool g_pinning_renderer = true;
bool g_in_webview_apply_attr = false;
bool g_in_webview_apply_attr_allow_nw = false;
} //namespace

#if defined(OS_MAC)
typedef void (*SendEventToAppFn)(const std::string& event_name, std::unique_ptr<base::ListValue> event_args);
CONTENT_EXPORT SendEventToAppFn gSendEventToApp = nullptr;

bool GetDirUserData(base::FilePath*);

void SetTrustAnchors(net::CertificateList&);
#endif
// browser

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());
  std::unique_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;
}

void MainPartsPostDestroyThreadsHook() {
  ReleaseNWPackage();
}

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

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());
  std::unique_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;
}

#if defined(OS_MAC)
CONTENT_EXPORT bool ApplicationShouldHandleReopenHook(bool hasVisibleWindows) {
  std::unique_ptr<base::ListValue> arguments(new base::ListValue());
  if (gSendEventToApp)
    gSendEventToApp("nw.App.onReopen",std::move(arguments));
  return true;
}

CONTENT_EXPORT void OSXOpenURLsHook(const std::vector<GURL>& startup_urls) {
  std::unique_ptr<base::ListValue> arguments(new base::ListValue());
  for (size_t i = 0; i < startup_urls.size(); i++)
    arguments->AppendString(startup_urls[i].spec());
  if (gSendEventToApp)
    gSendEventToApp("nw.App.onOpen", std::move(arguments));
}
#endif

void OverrideWebkitPrefsHook(content::RenderViewHost* rvh, content::WebPreferences* web_prefs) {
  nw::Package* package = nw::package();
  if (!package)
    return;
  base::DictionaryValue* webkit;
  web_prefs->plugins_enabled = true;
  if (package->root()->GetDictionary(switches::kmWebkit, &webkit)) {
    webkit->GetBoolean("double_tap_to_zoom_enabled", &web_prefs->double_tap_to_zoom_enabled);
    webkit->GetBoolean("plugin",                     &web_prefs->plugins_enabled);
  }
#if 0
  FilePath plugins_dir = package->path();

  plugins_dir = plugins_dir.AppendASCII("plugins");

  content::PluginService* plugin_service = content::PluginService::GetInstance();
  plugin_service->AddExtraPluginDir(plugins_dir);
#endif
}

#if 0
bool ShouldServiceRequestHook(int child_id, const GURL& url) {
  RenderProcessHost* rph = RenderProcessHost::FromID(child_id);
  if (!rph)
    return false;
  return RphGuestFilterURLHook(rph, &url);
}
#endif

bool PinningRenderer() {
  return g_pinning_renderer;
}

void SetPinningRenderer(bool pin) {
  g_pinning_renderer = pin;
}

void SetInWebViewApplyAttr(bool flag, bool allow_nw) {
  g_in_webview_apply_attr = flag;
  g_in_webview_apply_attr_allow_nw = allow_nw;
}

bool GetInWebViewApplyAttr(bool* allow_nw) {
  if (allow_nw)
    *allow_nw = g_in_webview_apply_attr_allow_nw;
  return g_in_webview_apply_attr;
}

} //namespace nw


var mkdirp = require('../').mkdirp;
var path = require('path');
var fs = require('fs');
var test = require('tap').test;
var _0755 = parseInt('0755', 8);

var ps = [ '', 'tmp' ];

for (var i = 0; i < 25; i++) {
    var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16);
    ps.push(dir);
}

var file = ps.join('/');

// a file in the way
var itw = ps.slice(0, 3).join('/');

test('clobber-pre', function (t) {
    console.error("about to write to "+itw)
    fs.writeFileSync(itw, 'I AM IN THE WAY, THE TRUTH, AND THE LIGHT.');

    fs.stat(itw, function (er, stat) {
        t.ifError(er)
        t.ok(stat && stat.isFile(), 'should be file')
        t.end()
    })
})

test('clobber', function (t) {
    t.plan(2);
    mkdirp(file, _0755, function (err) {
        t.ok(err);
        t.equal(err.code, 'ENOTDIR');
        t.end();
    });
});


function output(id, msg) {
  var h1 = document.createElement('h1');
  h1.innerHTML = msg;
  h1.setAttribute('id', id);
  document.body.appendChild(h1);
}

function success(id, msg) {
  output(id, 'success' + (msg ? ': ' + msg: ''));
}

function fail(id, msg) {
  output(id, 'fail' + (msg ? ': ' + msg: ''));
}


# MenuItem {: .doctitle}
---

[TOC]

`MenuItem` represents an item in a menu. A `MenuItem` can be a separator or a normal item which has label and icon or a checkbox. It can response to mouse click or keyboard shortcut.

## Synopsis

```javascript
var item;

// Create a separator
item = new nw.MenuItem({ type: 'separator' });

// Create a normal item with label and icon
item = new nw.MenuItem({
  type: "normal",
  label: "I'm a menu item",
  icon: "img/icon.png"
});

// Or you can omit the 'type' field for normal items
item = new nw.MenuItem({ label: 'Simple item' });

// Bind a callback to item
item = new nw.MenuItem({
  label: "Click me",
  click: function() {
    console.log("I'm clicked");
  },
  key: "s",
  modifiers: "ctrl+alt",
});

// You can have submenu!
var submenu = new nw.Menu();
submenu.append(new nw.MenuItem({ label: 'Item 1' }));
submenu.append(new nw.MenuItem({ label: 'Item 2' }));
submenu.append(new nw.MenuItem({ label: 'Item 3' }));
item.submenu = submenu;

// And everything can be changed at runtime
item.label = 'New label';
item.click = function() { console.log('New click callback'); };
```

## new MenuItem(option)

* `option` `{Object}` an object contains initial settings for the `MenuItem`
    - `label` `{String}` _Optional_ label for normal item or checkbox
    - `icon` `{String}` _Optional_ icon for normal item or checkbox
    - `tooltip` `{String}` _Optional_ tooltip for normal item or checkbox
    - `type` `{String}` _Optional_ the type of the item. Three types are accepted: `normal`, `checkbox`, `separator`
    - `click` `{Function}` _Optional_ the callback function when item is triggered by mouse click or keyboard shortcut
    - `enabled` `{Boolean}` _Optional_ whether the item is enabled or disabled. It's set to `true` by default.
    - `checked` `{Boolean}` _Optional_ whether the checkbox is checked or not. It's set to `false` by default.
    - `submenu` `{Menu}` _Optional_ a submenu
    - `key` `{String}` _Optional_ the key of the shortcut
    - `modifiers` `{String}` _Optional_ the modifiers of the shortcut

Every field has its own property in the `MenuItem`, see documentation of each property for details.

`MenuItem` is inherited from `EventEmitter`. You can use `on` to listen to the events.

## item.type

Get the `type` of a `MenuItem`, it can be `separator`, `checkbox` and `normal`.

!!! note
    The type can be set only when you create it. It cannot be changed at runtime.

## item.label

Get or set the `label` of a `MenuItem`, it can only be plain text for now.

## item.icon

Get or set the `icon` of a `MenuItem`, it must a path to your icon file. It can be a relative path which points to an icon in your app, or an absolute path pointing to a file in user's system.

It has no effect on setting `icon` of a `separator` item.

## item.iconIsTemplate (Mac)

Get or set whether `icon` image is treated as "template" (`true` by default). When the property is set to `true` the image is treated as "template" and the system automatically ensures proper styling according to the various states of the status item (e.g. dark menu, light menu, etc.). Template images should consist only of black and clear colours and can use the alpha channel in the image to adjust the opacity of black content. It has no effects on Linux and Windows.

## item.tooltip (Mac)

Get or set the `tooltip` of a `MenuItem`, it can only be plain text. A `tooltip` is short string that describes the menu item, it will show when you hover your mouse on the item.

## item.checked

Get or set whether the `MenuItem` is `checked`. Usually if a `MenuItem` is checked. There will be a mark on the left side of it. It's used mostly to indicate whether an option is on.

## item.enabled

Get or set whether the `MenuItem` is `enabled`. An disabled `MenuItem` will be greyed out and you will not be able to click on it.

## item.submenu

Get or set the `submenu` of a `MenuItem`, the `submenu` must be a `Menu` object. You should set `submenu` in the `option` when creating the `MenuItem`. Changing it at runtime is slow on some platforms.

## item.click

Get or set the `click` callback of a `MenuItem`, the `click` must be a valid function. It will be called when users activate the item.

## item.key

A single character string to specify the shortcut key for the menu item.

### Valid Keys for All Platforms

* Alphabet: `a`-`z`
* Digits: `0`-`9`
* Other keys on main area: `[` , `]` , `'` , `,` , `.` , `/` , `` ` `` , `-` , `=` , `\` , `'` , `;` , `Tab`
* `Esc`
* `Down` , `Up` , `Left` , `Right`
* [W3C DOM Level 3 KeyboardEvent Key Values](http://www.w3.org/TR/DOM-Level-3-Events-key/): `KeyA` (same as `A`), `Escape` (same as `Esc`), `F1`, `ArrowDown` (same as `Down`) etc.

### Special Keys for Mac Only
On Mac, you can also use special keys as shortcut key with `String.fromCharCode(specialKey)`. Here are some useful keys:

* **28**: Left (←)
* **29**: Right (→)
* **30**: Up (↑)
* **31**: Down (↓)
* **27**: Escape (⎋)
* **11**: PageUp (⇞)
* **12**: PageDown (⇟)

For full list of special keys supported on Mac, see [NSMenuItem.keyEquivalent](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSMenuItem_Class/#//apple_ref/occ/instp/NSMenuItem/keyEquivalent) and [NSEvent: Function-Key Unicodes](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSEvent_Class/index.html#//apple_ref/doc/constant_group/Function_Key_Unicodes).

## item.modifiers

A string to specify the modifier keys for the shortcut of the menu item. It should be the concatenation of the following strings: `cmd` / `command` / `super`, `shift`, `ctrl`, `alt`. e.g. `"cmd+shift+alt"`.

`cmd` represents different keys on all platforms: Windows key (Windows) on Windows and Linux, Apple key (⌘) on Mac. `super` and `command` are aliases of `cmd`.

## Event: click

Emitted when user activates the menu item.


bool g_in_webview_apply_attr_allow_nw = false;

// Copyright 2015 The NW.js Authors. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.

// nw Window API
namespace nw.Window {
  callback CreateWindowCallback =
      void ([instanceOf=NWWindow] object createdWindow);
  callback EventCallback = void();
  callback GetAllWinCallback = void (object[] windows);

  callback CapturePageCallback = void (DOMString dataUrl);
  [noinline_doc] dictionary CapturePageOptions {
    [nodoc] DOMString? format;
    [nodoc] DOMString? datatype;
    [nodoc] long? quality;
  };
  [noinline_doc] dictionary CreateWindowOptions {
    [nodoc] DOMString? id;
    [nodoc] long? x;
    [nodoc] long? y;
    [nodoc] long? width;
    [nodoc] long? height;
    [nodoc] long? min_width;
    [nodoc] long? min_height;
    [nodoc] long? max_width;
    [nodoc] long? max_height;
    [nodoc] boolean? frame;
    [nodoc] boolean? resizable;
    [nodoc] boolean? fullscreen;
    [nodoc] boolean? show;
    [nodoc] boolean? always_on_top;
    [nodoc] boolean? visible_on_all_workspaces;
    [nodoc] boolean? transparent;
    [nodoc] boolean? kiosk;
    [nodoc] boolean? focus;
    [nodoc] boolean? new_instance;
    [nodoc] boolean? show_in_taskbar;
    [nodoc] DOMString? title;
    [nodoc] DOMString? position;
    [nodoc] DOMString? icon;
    [nodoc] DOMString? inject_js_start;
    [nodoc] DOMString? inject_js_end;
  };

  [noinline_doc] dictionary NWWindow {
    // show devtools for the current window
    static void showDevTools(optional any frm, optional EventCallback callback);
    static void capturePage(optional CapturePageCallback callback, optional CapturePageOptions options);
    static void show();
    static void hide();
    static void focus();
    static void blur();
    static void close(optional boolean force);
    static void minimize();
    static void maximize();
    static void unmaximize();
    static void restore();
    static void setPosition(DOMString pos);
    static void setMaximumSize(long width, long height);
    static void setMinimumSize(long width, long height);
    static void setResizable(boolean resizable);
    static void enterFullscreen();
    static void leaveFullscreen();
    static void toggleFullscreen();
    static void setVisibleOnAllWorkspaces(boolean all_visible);
    static bool canSetVisibleOnAllWorkspaces();
    static void on(DOMString event,
                   EventCallback callback);
    static void once(DOMString event,
                     EventCallback callback);
    static void removeListener(DOMString event,
                               EventCallback callback);
    static void removeAllListeners(DOMString event);
    static void reload();
    static void reloadIgnoringCache();

    static void eval(object frame, DOMString script);
    static void evalNWBin(object frame, DOMString path);
    static void evalNWBinModule(object frame, DOMString path, DOMString module_path);
    static void print(optional object options);

    object appWindow;
    object cWindow;
    object window;
    object menu;
    long x;
    long y;
    long width;
    long height;
    double zoomLevel;
    DOMString title;
    long frameId;
  };
  interface Functions {
    // get the current window
    [nocompile] static NWWindow get(optional object domWindow);
    [nocompile] static void getAll(GetAllWinCallback callback);
    [nocompile] static void open(DOMString url,
                                 optional CreateWindowOptions options,
                                 optional CreateWindowCallback callback);

  };
  interface Events {
    [nocompile] static void onNewWinPolicy();
    [nocompile] static void onNavigation();
    [nocompile] static void LoadingStateChanged();
    [nocompile] static void onDocumentStart();
    [nocompile] static void onDocumentEnd();
    [nocompile] static void onZoom();
    [supportsFilters=true] static void onClose();
  };
};


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

#include "content/nw/src/media/media_stream_devices_controller.h"

#include "base/command_line.h"
#include "base/values.h"
#include "chrome/common/chrome_switches.h"
#include "content/nw/src/media/media_capture_devices_dispatcher.h"
#include "content/nw/src/media/media_internals.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/desktop_media_id.h"
#include "content/public/common/media_stream_request.h"

#include "third_party/webrtc/modules/desktop_capture/desktop_capture_types.h"

using content::BrowserThread;

namespace {

bool HasAnyAvailableDevice() {
  MediaCaptureDevicesDispatcher* dispatcher =
      MediaInternals::GetInstance()->GetMediaCaptureDevicesDispatcher();
  const content::MediaStreamDevices& audio_devices =
      dispatcher->GetAudioCaptureDevices();
  const content::MediaStreamDevices& video_devices =
      dispatcher->GetVideoCaptureDevices();

  return !audio_devices.empty() || !video_devices.empty();
};

//const char kAudioKey[] = "audio";
//const char kVideoKey[] = "video";

}  // namespace

MediaStreamDevicesController::MediaStreamDevicesController(
    const content::MediaStreamRequest& request,
    const content::MediaResponseCallback& callback)
    :
      request_(request),
      callback_(callback),
      has_audio_(content::IsAudioInputMediaType(request.audio_type) &&
                 !IsAudioDeviceBlockedByPolicy()),
      has_video_(content::IsVideoMediaType(request.video_type) &&
                 !IsVideoDeviceBlockedByPolicy()) {
}

MediaStreamDevicesController::~MediaStreamDevicesController() {}

#if 0
// static
void MediaStreamDevicesController::RegisterUserPrefs(
    PrefServiceSyncable* prefs) {
  prefs->RegisterBooleanPref(prefs::kVideoCaptureAllowed,
                             true,
                             PrefServiceSyncable::UNSYNCABLE_PREF);
  prefs->RegisterBooleanPref(prefs::kAudioCaptureAllowed,
                             true,
                             PrefServiceSyncable::UNSYNCABLE_PREF);
}
#endif

bool MediaStreamDevicesController::DismissInfoBarAndTakeActionOnSettings() {
  // If this is a no UI check for policies only go straight to accept - policy
  // check will be done automatically on the way.
  if (request_.request_type == content::MEDIA_OPEN_DEVICE) {
    Accept(false);
    return true;
  }

  if (request_.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE ||
      request_.video_type == content::MEDIA_TAB_VIDEO_CAPTURE ||
      request_.video_type == content::MEDIA_DESKTOP_VIDEO_CAPTURE) {
    HandleTapMediaRequest();
    return true;
  }

#if 0
  // Deny the request if the security origin is empty, this happens with
  // file access without |--allow-file-access-from-files| flag.
  if (request_.security_origin.is_empty()) {
    Deny(false);
    return true;
  }
#endif
  // Deny the request if there is no device attached to the OS.
  if (!HasAnyAvailableDevice()) {
    Deny(false);
    return true;
  }

  // Check if any allow exception has been made for this request.
  if (IsRequestAllowedByDefault()) {
    Accept(false);
    return true;
  }
#if 0
  // Check if any block exception has been made for this request.
  if (IsRequestBlockedByDefault()) {
    Deny(false);
    return true;
  }

  // Check if the media default setting is set to block.
  if (IsDefaultMediaAccessBlocked()) {
    Deny(false);
    return true;
  }
#endif
  // Don't show the infobar.
  return true;
}

const std::string& MediaStreamDevicesController::GetSecurityOriginSpec() const {
  return request_.security_origin.spec();
}

void MediaStreamDevicesController::Accept(bool update_content_setting) {
  // Get the default devices for the request.
  content::MediaStreamDevices devices;
  MediaCaptureDevicesDispatcher* dispatcher =
      MediaInternals::GetInstance()->GetMediaCaptureDevicesDispatcher();
  switch (request_.request_type) {
  case content::MEDIA_OPEN_DEVICE: {
    const content::MediaStreamDevice* device = NULL;
    // For open device request pick the desired device or fall back to the
    // first available of the given type.
    if (request_.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE) {
      device = dispatcher->
        GetRequestedAudioDevice(request_.requested_audio_device_id);
      // TODO(wjia): Confirm this is the intended behavior.
      if (!device) {
        device = dispatcher->GetFirstAvailableAudioDevice();
      }
    } else if (request_.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE) {
      // Pepper API opens only one device at a time.
      device = dispatcher->GetRequestedVideoDevice(request_.requested_video_device_id);
      // TODO(wjia): Confirm this is the intended behavior.
      if (!device) {
        device = dispatcher->GetFirstAvailableVideoDevice();
      }
    }
    if (device)
      devices.push_back(*device);
    break;
  } case content::MEDIA_GENERATE_STREAM: {
      bool needs_audio_device = has_audio_;
      bool needs_video_device = has_video_;

      // Get the exact audio or video device if an id is specified.
      if (!request_.requested_audio_device_id.empty()) {
        const content::MediaStreamDevice* audio_device =
          dispatcher->GetRequestedAudioDevice(request_.requested_audio_device_id);
        if (audio_device) {
          devices.push_back(*audio_device);
          needs_audio_device = false;
        }
      }
      if (!request_.requested_video_device_id.empty()) {
        const content::MediaStreamDevice* video_device =
          dispatcher->GetRequestedVideoDevice(request_.requested_video_device_id);
        if (video_device) {
          devices.push_back(*video_device);
          needs_video_device = false;
        }
      }

      // If either or both audio and video devices were requested but not
      // specified by id, get the default devices.
      if (needs_audio_device || needs_video_device) {
        media::GetDefaultDevicesForProfile(
                                      needs_audio_device,
                                      needs_video_device,
                                      &devices);
      }
      break;
    } case content::MEDIA_DEVICE_ACCESS:
    // Get the default devices for the request.
    media::GetDefaultDevicesForProfile(
                                  has_audio_,
                                  has_video_,
                                  &devices);
    break;
  case content::MEDIA_ENUMERATE_DEVICES:
    // Do nothing.
    NOTREACHED();
    break;
  }

  callback_.Run(devices,
                devices.empty() ?
                content::MEDIA_DEVICE_NO_HARDWARE : content::MEDIA_DEVICE_OK,
                scoped_ptr<content::MediaStreamUI>());
}

void MediaStreamDevicesController::Deny(bool update_content_setting) {
  callback_.Run(content::MediaStreamDevices(), content::MEDIA_DEVICE_NO_HARDWARE, scoped_ptr<content::MediaStreamUI>());
}

bool MediaStreamDevicesController::IsAudioDeviceBlockedByPolicy() const {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  return false;
}

bool MediaStreamDevicesController::IsVideoDeviceBlockedByPolicy() const {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  return false;
}

bool MediaStreamDevicesController::IsRequestAllowedByDefault() const {
  return true;
}

bool MediaStreamDevicesController::IsRequestBlockedByDefault() const {
  return false;
}

bool MediaStreamDevicesController::IsDefaultMediaAccessBlocked() const {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  return false;
}

void MediaStreamDevicesController::HandleTapMediaRequest() {
  content::MediaStreamDevices devices;

  if (request_.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE) {
    devices.push_back(content::MediaStreamDevice(
          content::MEDIA_TAB_VIDEO_CAPTURE, "", ""));
  }
  if (request_.video_type == content::MEDIA_TAB_VIDEO_CAPTURE) {
    devices.push_back(content::MediaStreamDevice(
          content::MEDIA_TAB_AUDIO_CAPTURE, "", ""));
  }
  if (request_.video_type == content::MEDIA_DESKTOP_VIDEO_CAPTURE) {
    const bool screen_capture_enabled =
      CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kEnableUserMediaScreenCapturing);

    if (screen_capture_enabled) {
      content::DesktopMediaID media_id;
      // If the device id wasn't specified then this is a screen capture request
      // (i.e. chooseDesktopMedia() API wasn't used to generate device id).
      if (request_.requested_video_device_id.empty()) {
        media_id =
            content::DesktopMediaID(content::DesktopMediaID::TYPE_SCREEN,
                                    webrtc::kFullDesktopScreenId);
      } else {
        media_id =
            content::DesktopMediaID::Parse(request_.requested_video_device_id);
      }

      devices.push_back(content::MediaStreamDevice(
            content::MEDIA_DESKTOP_VIDEO_CAPTURE, media_id.ToString(), "Screen"));
    }
  }

  callback_.Run(devices,
                devices.empty() ?
                     content::MEDIA_DEVICE_NO_HARDWARE : content::MEDIA_DEVICE_OK,
                scoped_ptr<content::MediaStreamUI>());
}

bool MediaStreamDevicesController::IsSchemeSecure() const {
  return (request_.security_origin.SchemeIsSecure());
}

bool MediaStreamDevicesController::ShouldAlwaysAllowOrigin() const {
  return true;
}


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

#ifndef EXTENSIONS_SHELL_BROWSER_MEDIA_CAPTURE_UTIL_H_
#define EXTENSIONS_SHELL_BROWSER_MEDIA_CAPTURE_UTIL_H_

#include "base/macros.h"
#include "content/public/common/media_stream_request.h"

namespace content {
class WebContents;
}

namespace extensions {

class Extension;

namespace media_capture_util {

// Grants access to audio and video capture devices.
// * If the caller requests specific device ids, grants access to those.
// * If the caller does not request specific ids, grants access to the first
//   available device.
// Usually used as a helper for media capture ProcessMediaAccessRequest().
void GrantMediaStreamRequest(content::WebContents* web_contents,
                             const content::MediaStreamRequest& request,
                             const content::MediaResponseCallback& callback,
                             const Extension* extension);

// Verifies that the extension has permission for |type|. If not, crash.
void VerifyMediaAccessPermission(content::MediaStreamType type,
                                 const Extension* extension);

}  // namespace media_capture_util
}  // namespace extensions

#endif  // EXTENSIONS_SHELL_BROWSER_MEDIA_CAPTURE_UTIL_H_


var tap = require("tap")
  , tar = require("../tar.js")
  , fs = require("fs")
  , path = require("path")
  , file = path.resolve(__dirname, "fixtures/c.tar")
  , index = 0

  , expect =
[ [ 'entry',
    { path: 'c.txt',
      mode: 420,
      uid: 24561,
      gid: 20,
      size: 513,
      mtime: new Date('Wed, 26 Oct 2011 01:10:58 GMT'),
      cksum: 5422,
      type: '0',
      linkpath: '',
      ustar: 'ustar\0',
      ustarver: '00',
      uname: 'isaacs',
      gname: 'staff',
      devmaj: 0,
      devmin: 0,
      fill: '' },
    undefined ],
  [ 'entry',
    { path: 'cc.txt',
      mode: 420,
      uid: 24561,
      gid: 20,
      size: 513,
      mtime: new Date('Wed, 26 Oct 2011 01:11:02 GMT'),
      cksum: 5525,
      type: '0',
      linkpath: '',
      ustar: 'ustar\0',
      ustarver: '00',
      uname: 'isaacs',
      gname: 'staff',
      devmaj: 0,
      devmin: 0,
      fill: '' },
    undefined ],
  [ 'entry',
    { path: 'r/e/a/l/l/y/-/d/e/e/p/-/f/o/l/d/e/r/-/p/a/t/h/cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc',
      mode: 420,
      uid: 24561,
      gid: 20,
      size: 100,
      mtime: new Date('Thu, 27 Oct 2011 03:43:23 GMT'),
      cksum: 18124,
      type: '0',
      linkpath: '',
      ustar: 'ustar\0',
      ustarver: '00',
      uname: 'isaacs',
      gname: 'staff',
      devmaj: 0,
      devmin: 0,
      fill: '' },
    undefined ],
  [ 'entry',
    { path: 'Ω.txt',
      mode: 420,
      uid: 24561,
      gid: 20,
      size: 2,
      mtime: new Date('Thu, 27 Oct 2011 17:51:49 GMT'),
      cksum: 5695,
      type: '0',
      linkpath: '',
      ustar: 'ustar\0',
      ustarver: '00',
      uname: 'isaacs',
      gname: 'staff',
      devmaj: 0,
      devmin: 0,
      fill: '' },
    undefined ],
  [ 'extendedHeader',
    { path: 'PaxHeader/Ω.txt',
      mode: 420,
      uid: 24561,
      gid: 20,
      size: 120,
      mtime: new Date('Thu, 27 Oct 2011 17:51:49 GMT'),
      cksum: 6702,
      type: 'x',
      linkpath: '',
      ustar: 'ustar\0',
      ustarver: '00',
      uname: 'isaacs',
      gname: 'staff',
      devmaj: 0,
      devmin: 0,
      fill: '' },
    { path: 'Ω.txt',
      ctime: 1319737909,
      atime: 1319739061,
      dev: 234881026,
      ino: 51693379,
      nlink: 1 } ],
  [ 'entry',
    { path: 'Ω.txt',
      mode: 420,
      uid: 24561,
      gid: 20,
      size: 2,
      mtime: new Date('Thu, 27 Oct 2011 17:51:49 GMT'),
      cksum: 5695,
      type: '0',
      linkpath: '',
      ustar: 'ustar\0',
      ustarver: '00',
      uname: 'isaacs',
      gname: 'staff',
      devmaj: 0,
      devmin: 0,
      fill: '',
      ctime: new Date('Thu, 27 Oct 2011 17:51:49 GMT'),
      atime: new Date('Thu, 27 Oct 2011 18:11:01 GMT'),
      dev: 234881026,
      ino: 51693379,
      nlink: 1 },
    undefined ],
  [ 'extendedHeader',
    { path: 'PaxHeader/200ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc',
      mode: 420,
      uid: 24561,
      gid: 20,
      size: 353,
      mtime: new Date('Thu, 27 Oct 2011 03:41:08 GMT'),
      cksum: 14488,
      type: 'x',
      linkpath: '',
      ustar: 'ustar\0',
      ustarver: '00',
      uname: 'isaacs',
      gname: 'staff',
      devmaj: 0,
      devmin: 0,
      fill: '' },
    { path: '200ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc',
      ctime: 1319686868,
      atime: 1319741254,
      'LIBARCHIVE.creationtime': '1319686852',
      dev: 234881026,
      ino: 51681874,
      nlink: 1 } ],
  [ 'entry',
    { path: '200ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc',
      mode: 420,
      uid: 24561,
      gid: 20,
      size: 200,
      mtime: new Date('Thu, 27 Oct 2011 03:41:08 GMT'),
      cksum: 14570,
      type: '0',
      linkpath: '',
      ustar: 'ustar\0',
      ustarver: '00',
      uname: 'isaacs',
      gname: 'staff',
      devmaj: 0,
      devmin: 0,
      fill: '',
      ctime: new Date('Thu, 27 Oct 2011 03:41:08 GMT'),
      atime: new Date('Thu, 27 Oct 2011 18:47:34 GMT'),
      'LIBARCHIVE.creationtime': '1319686852',
      dev: 234881026,
      ino: 51681874,
      nlink: 1 },
    undefined ],
  [ 'longPath',
    { path: '././@LongLink',
      mode: 0,
      uid: 0,
      gid: 0,
      size: 201,
      mtime: new Date('Thu, 01 Jan 1970 00:00:00 GMT'),
      cksum: 4976,
      type: 'L',
      linkpath: '',
      ustar: false },
    '200ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc' ],
  [ 'entry',
    { path: '200ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc',
      mode: 420,
      uid: 1000,
      gid: 1000,
      size: 201,
      mtime: new Date('Thu, 27 Oct 2011 22:21:50 GMT'),
      cksum: 14086,
      type: '0',
      linkpath: '',
      ustar: false },
    undefined ],
  [ 'longLinkpath',
    { path: '././@LongLink',
      mode: 0,
      uid: 0,
      gid: 0,
      size: 201,
      mtime: new Date('Thu, 01 Jan 1970 00:00:00 GMT'),
      cksum: 4975,
      type: 'K',
      linkpath: '',
      ustar: false },
    '200ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc' ],
  [ 'longPath',
    { path: '././@LongLink',
      mode: 0,
      uid: 0,
      gid: 0,
      size: 201,
      mtime: new Date('Thu, 01 Jan 1970 00:00:00 GMT'),
      cksum: 4976,
      type: 'L',
      linkpath: '',
      ustar: false },
    '200LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL' ],
  [ 'entry',
    { path: '200LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL',
      mode: 511,
      uid: 1000,
      gid: 1000,
      size: 0,
      mtime: new Date('Fri, 28 Oct 2011 23:05:17 GMT'),
      cksum: 21603,
      type: '2',
      linkpath: '200ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc',
      ustar: false },
    undefined ],
  [ 'extendedHeader',
    { path: 'PaxHeader/200-hard',
      mode: 420,
      uid: 24561,
      gid: 20,
      size: 143,
      mtime: new Date('Thu, 27 Oct 2011 03:41:08 GMT'),
      cksum: 6533,
      type: 'x',
      linkpath: '',
      ustar: 'ustar\0',
      ustarver: '00',
      uname: 'isaacs',
      gname: 'staff',
      devmaj: 0,
      devmin: 0,
      fill: '' },
    { ctime: 1320617144,
      atime: 1320617232,
      'LIBARCHIVE.creationtime': '1319686852',
      dev: 234881026,
      ino: 51681874,
      nlink: 2 } ],
  [ 'entry',
    { path: '200-hard',
      mode: 420,
      uid: 24561,
      gid: 20,
      size: 200,
      mtime: new Date('Thu, 27 Oct 2011 03:41:08 GMT'),
      cksum: 5526,
      type: '0',
      linkpath: '',
      ustar: 'ustar\0',
      ustarver: '00',
      uname: 'isaacs',
      gname: 'staff',
      devmaj: 0,
      devmin: 0,
      fill: '',
      ctime: new Date('Sun, 06 Nov 2011 22:05:44 GMT'),
      atime: new Date('Sun, 06 Nov 2011 22:07:12 GMT'),
      'LIBARCHIVE.creationtime': '1319686852',
      dev: 234881026,
      ino: 51681874,
      nlink: 2 },
    undefined ],
  [ 'extendedHeader',
    { path: 'PaxHeader/200ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc',
      mode: 420,
      uid: 24561,
      gid: 20,
      size: 353,
      mtime: new Date('Thu, 27 Oct 2011 03:41:08 GMT'),
      cksum: 14488,
      type: 'x',
      linkpath: '',
      ustar: 'ustar\0',
      ustarver: '00',
      uname: 'isaacs',
      gname: 'staff',
      devmaj: 0,
      devmin: 0,
      fill: '' },
    { path: '200ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc',
      ctime: 1320617144,
      atime: 1320617406,
      'LIBARCHIVE.creationtime': '1319686852',
      dev: 234881026,
      ino: 51681874,
      nlink: 2 } ],
  [ 'entry',
    { path: '200ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc',
      mode: 420,
      uid: 24561,
      gid: 20,
      size: 0,
      mtime: new Date('Thu, 27 Oct 2011 03:41:08 GMT'),
      cksum: 15173,
      type: '1',
      linkpath: '200-hard',
      ustar: 'ustar\0',
      ustarver: '00',
      uname: 'isaacs',
      gname: 'staff',
      devmaj: 0,
      devmin: 0,
      fill: '',
      ctime: new Date('Sun, 06 Nov 2011 22:05:44 GMT'),
      atime: new Date('Sun, 06 Nov 2011 22:10:06 GMT'),
      'LIBARCHIVE.creationtime': '1319686852',
      dev: 234881026,
      ino: 51681874,
      nlink: 2 },
    undefined ] ]

tap.test("parser test", function (t) {
  var parser = tar.Parse()

  parser.on("end", function () {
    t.equal(index, expect.length, "saw all expected events")
    t.end()
  })

  fs.createReadStream(file)
    .pipe(parser)
    .on("*", function (ev, entry) {
      var wanted = expect[index]
      if (!wanted) {
        return t.fail("Unexpected event: " + ev)
      }
      var result = [ev, entry.props]
      entry.on("end", function () {
        result.push(entry.fields || entry.body)

        t.equal(ev, wanted[0], index + " event type")
        t.equivalent(entry.props, wanted[1], wanted[1].path + " entry properties")
        if (wanted[2]) {
          t.equivalent(result[2], wanted[2], "metadata values")
        }
        index ++
      })
    })
})


void SetInWebViewApplyAttr(bool flag, bool allow_nw) {

keep keep keep keep add keep keep keep keep

bool MixedContext() {
return g_mixed_context;
}
void SetMixedContext(bool mixed) {
g_mixed_context = mixed;
}

#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


var colors = require('../colors');

module['exports'] = (function () {
  var available = ['underline', 'inverse', 'grey', 'yellow', 'red', 'green', 'blue', 'white', 'cyan', 'magenta'];
  return function(letter, i, exploded) {
    return letter === " " ? letter : colors[available[Math.round(Math.random() * (available.length - 1))]](letter);
  };
})();


#!/usr/bin/env python

import SimpleHTTPServer
import SocketServer
import sys

PORT = int(sys.argv[1])

Handler = SimpleHTTPServer.SimpleHTTPRequestHandler

httpd = SocketServer.TCPServer(("", PORT), Handler)

print "serving at port", PORT
httpd.serve_forever()


import time
import os
import platform
import sys

if platform.system() == 'Linux':
  print 'Skiping win-move because of upstream issue. See nwjs/nw.js#4217.'
  sys.exit(0)

from selenium import webdriver
from selenium.webdriver.chrome.options import Options

chrome_options = Options()
chrome_options.add_argument("nwapp=" + os.path.dirname(os.path.abspath(__file__)))

driver = webdriver.Chrome(executable_path=os.environ['CHROMEDRIVER'], chrome_options=chrome_options)
driver.implicitly_wait(10)
try:
    print driver.current_url
    driver.find_element_by_id('moveto').click()
    result = driver.find_element_by_id('moveto-result').get_attribute('innerHTML')
    print result
    assert('success' in result)

    driver.find_element_by_id('moveby').click()
    result = driver.find_element_by_id('moveby-result').get_attribute('innerHTML')
    print result
    assert('success' in result)
finally:
    driver.quit()


// ref in chrome/browser/app_controller_mac.mm

CONTENT_EXPORT bool ApplicationShouldHandleReopenHook(bool hasVisibleWindows);