Archive

Archive for September, 2015

Alternative to JSONP for One-shot webservices #AJAX

Cross-domain restrictions prevent you from exchanging data from domainA.com to domainB.com , however, there are some workarounds, such as JSONP, where you trick the browser into loading a script from another domain, and have that script call back to your page – It does mean however, that the webservice has to be specifically designed to include the call back to your javascript callback function.

However, this is another workaround that does not require any specific format in the return value of the webservice, in effect, the return value is ignored, so it’s only for one-shot “fire and forget” webservices.

Here’s an example, where you can send email from javascript, using the webservice from www.directtomx.com 

Email = {
Send : function (to,from,subject,body,apikey)
{
if (apikey == undefined)
{
apikey = Email.apikey;
}
var nocache= Math.floor((Math.random() * 1000000) + 1);
var strUrl = “http://directtomx.azurewebsites.net/mx.asmx/Send?”;
strUrl += “apikey=” + apikey;
strUrl += “&from=” + from;
strUrl += “&to=” + to;
strUrl += “&subject=” + encodeURIComponent(subject);
strUrl += “&body=” + encodeURIComponent(body);
strUrl += “&cachebuster=” + nocache;
Email.addScript(strUrl);
},
apikey : “”,
addScript : function(src){
var s = document.createElement( ‘link’ );
s.setAttribute( ‘rel’, ‘stylesheet’ );
s.setAttribute( ‘type’, ‘text/xml’ );
s.setAttribute( ‘href’, src);
document.body.appendChild( s );
}
};

Then it would be called like this –

window.onload = function(){
Email.apikey = “– Your api key from directtomx.com —“;
Email.Send(“swordfish1234@printfromipad.com”,”info@webtropy.com”,”Sent from JS API”,”Worked!”);
}

It works by pretending that the webservice call is actually a CSS stylesheet. The CSS will be invalid, but this type of error would be swallowed silently by the browser.

Categories: Uncategorized

Backendless form using Parse and Bootstrap

If you want to collect information from users, but don’t need to act on the information right away, for example, a Survey, or unsubscribe form, then you’d think you’d need to start creating a database table, connecting up server side code, etc, etc.,

This is obviously one way to go about it, but sometimes, you’d like a quicker solution, that just uses javascript code, on the client side, and wouldn’t it be nice if I didn’t have to upload Bootstrap, jquery, etc., to the server, and load everything from CDNs?, so something that would typically require a database, business layer code, hundreds of images, css, and javascript libraries can just be deployed with one file?

Which is where Parse comes in. You can use it to store and retrieve arbitrary javascript objects in a persistent manner, so that you don’t have to create a database or middleware.

So, I have a simple unsubscribe form, where I want to capture and store users’ email addresses, and then I’ll export them back out of Parse whenever it comes time to remove bad emails from my list.

Here’s the code in javascript;

$(init);
function init()
{
Parse.initialize(“xxx”, “xxxx”);
$(“#btnReport”).bind(“click”,btnReport_click);
}
function btnReport_click()
{
var Unsubscription = Parse.Object.extend(“Unsubscription”);
var unsubscription = new Unsubscription();
unsubscription.save({email: $(“#ReportedBy”).val()}).then(function(object) {
bootbox.alert(“Thanks, we’ve recorded your request.”);
});
}

I’ve replaced my Parse keys with xxx – you’ll need to get your own. It creates a new Unsubscription object, then saves it with the property “email”, and lets the user know when it’s done.- This is then persistently stored on Parse’s servers.

And, the rest of the form, all loaded from CDN’s

<html >
<head>
<meta name=”viewport” content=”width=device-width, initial-scale=1″>
<link rel=”stylesheet” href=”http://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css”&gt;
http://code.jquery.com/jquery-1.11.3.js
http://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js
http://cdnjs.cloudflare.com/ajax/libs/bootbox.js/4.4.0/bootbox.js
http://www.parsecdn.com/js/parse-1.6.2.min.js

$(init);
function init()
{
Parse.initialize(“xxx”, “xxx”);
$(“#btnReport”).bind(“click”,btnReport_click);
}
function btnReport_click()
{
var Unsubscription = Parse.Object.extend(“Unsubscription”);
var unsubscription = new Unsubscription();
unsubscription.save({email: $(“#ReportedBy”).val()}).then(function(object) {
bootbox.alert(“Thanks, we’ve recorded your request.”);
});
}

</head>
<body>
<form class=”form-horizontal”>
<fieldset>
<legend>Unsubscibe</legend>

Your email

If you do not want to receive any more email from us, just enter it here.

</div>

</div>
</fieldset>
</form>
</body>

Categories: Uncategorized

Validation in Windows phone, flash textbox red if invalid =WP8

Here’s some nice code to flash a textbox red if the input is invalid – It’s tested in WP8.1

private void Warn(Control target)
{
var elementColour = ((SolidColorBrush)target.Background).Color;
ColorAnimation ca1 = new ColorAnimation();
ca1.Duration = new Duration(TimeSpan.FromSeconds(0.5));
ca1.From = elementColour;
ca1.To = Color.FromArgb(255, 255, 0, 0);
Storyboard.SetTarget(ca1, target);
Storyboard.SetTargetProperty(ca1, new PropertyPath(“UIElement.Background.Color”));
// alternative tested PropertyPath
// “Panel.Background.Color”
// “Panel.Background.SolidColorBrush.Color”
// “(Panel.Background).(SolidColorBrush.Color)”
Storyboard sb = new Storyboard();
sb.Children.Add(ca1);
sb.Begin();
sb.Completed += (a, b) =>
{
ColorAnimation ca2 = new ColorAnimation();
ca2.BeginTime = TimeSpan.FromSeconds(1);
ca2.Duration = new Duration(TimeSpan.FromSeconds(0.5));
ca2.From = Color.FromArgb(255, 255, 0, 0);
ca2.To = elementColour;
Storyboard.SetTarget(ca2, target);
Storyboard.SetTargetProperty(ca2, new PropertyPath(“UIElement.Background.Color”));
Storyboard sb2 = new Storyboard();
sb2.Children.Add(ca2);
sb2.Begin();
};
}

Categories: Uncategorized

Generate #pkpass – Apple Passbook coupons from C#

This is quite a long lump of code, and it has some prerequisites, that you’ll need to do first.

1. you’ll need to have all your graphic assets in a folder, pre-prepared

2. You’ll need to install the apple root cert into your cert store, from here http://www.apple.com/appleca/AppleIncRootCertificate.cer

3. And, the hardest step, you’ll need to generate a p12 file for your passbook type, – here’s a good tutorial to do this;

http://www.raywenderlich.com/20734/beginning-passbook-part-1 – or

https://blog.dotnetframework.org/2015/09/21/creating-apple-passbook-pkpass-coupons-from-windows/

  • Now, first I’m going to declare some useful objects

using System.Collections.Generic;
using System.Linq;

namespace libPkPass
{
/// <summary>
/// Objects representing the JSON files contained in the .pkpass file
/// </summary>
public class JsonObjects
{
/// <summary>
/// The image assets used to make the coupon more attractive
/// </summary>
public enum AssetTypes
{
/// <summary>
/// 320 x 122
/// </summary>
strippng,
/// <summary>
/// 640 x 244
/// </summary>
stripAt2xpng,
/// <summary>
/// 29 x 29
/// </summary>
iconpng,
/// <summary>
/// 58 x 58
/// </summary>
iconAt2xpng,
/// <summary>
/// 50 x 50
/// </summary>
logopng,
/// <summary>
/// 100 x 100
/// </summary>
logoAt2xpng,
/// <summary>
/// Describes the text content of the coupon
/// </summary>
passjson
}
/// <summary>
/// The Manifest (contents of the zip)
/// </summary>
public class Manifest
{

/// <summary>
/// Gets or sets the strip.png hash
/// </summary>
/// <value>
/// The strip.png.
/// </value>
public string strippng { get; set; }

/// <summary>
/// Gets or sets the strip@2x.png hash.
/// </summary>
/// <value>
/// The strip@2x.png.
/// </value>
public string stripAt2xpng { get; set; }

/// <summary>
/// Gets or sets the icon.png hash
/// </summary>
/// <value>
/// The icon.png.
/// </value>
public string iconpng { get; set; }

/// <summary>
/// Gets or sets the icon@2x.png hash
/// </summary>
/// <value>
/// The icon icon@2x.png.
/// </value>
public string iconAt2xpng { get; set; }

/// <summary>
/// Gets or sets the logo.png hash.
/// </summary>
/// <value>
/// The logo.png hash.
/// </value>
public string logopng { get; set; }

/// <summary>
/// Gets or sets the logo@2x.png hash.
/// </summary>
/// <value>
/// The logo@2x.png.
/// </value>
public string logoAt2xpng { get; set; }

/// <summary>
/// Gets or sets the pass.json.
/// </summary>
/// <value>
/// The pass.json.
/// </value>
public string passjson { get; set; }

/// <summary>
/// Gets the asset for the given type
/// </summary>
/// <param name=”type”>The type.</param>
/// <returns></returns>
public string GetAsset(AssetTypes type)
{
return Map.First(m => m.type == type).asset;
}
/// <summary>
/// A map of assets to types
/// </summary>
public static List<ManifestMapping> Map = new List<ManifestMapping>
{
new ManifestMapping {asset = “strip.png”, type = AssetTypes.strippng, property = “stripping”},
new ManifestMapping {asset = “strip@2x.png”, type = AssetTypes.stripAt2xpng, property = “stripAt2xpng”},
new ManifestMapping {asset = “icon.png”, type = AssetTypes.iconpng, property = “iconpng”},
new ManifestMapping {asset = “icon@2x.png”, type = AssetTypes.iconAt2xpng, property = “iconAt2xpng”},
new ManifestMapping {asset = “logo.png”, type = AssetTypes.logopng, property = “logopng”},
new ManifestMapping {asset = “logo@2x.png”, type = AssetTypes.logoAt2xpng, property = “logoAt2xpng”},
new ManifestMapping {asset = “pass.json”, type = AssetTypes.passjson, property = “passjson”},
};
}
/// <summary>
/// Encapsulates a map of assets to fields
/// </summary>
public class ManifestMapping
{
/// <summary>
/// The type
/// </summary>
public AssetTypes type;
/// <summary>
/// The property
/// </summary>
public string property;
/// <summary>
/// The file name
/// </summary>
public string asset;
}
/// <summary>
/// The Barcode representation of the coupon
/// </summary>
public class Barcode
{
/// <summary>
/// Gets or sets the message.
/// </summary>
/// <value>
/// The message.
/// </value>
public string message { get; set; }
/// <summary>
/// Gets or sets the format.
/// </summary>
/// <value>
/// The format.
/// </value>
public string format { get; set; }
/// <summary>
/// Gets or sets the message encoding.
/// </summary>
/// <value>
/// The message encoding.
/// </value>
public string messageEncoding { get; set; }
}
/// <summary>
/// The general details of the coupon
/// </summary>
public class Pass
{
/// <summary>
/// Gets or sets the format version.
/// </summary>
/// <value>
/// The format version.
/// </value>
public int formatVersion { get; set; }
/// <summary>
/// Gets or sets the pass type identifier.
/// </summary>
/// <value>
/// The pass type identifier.
/// </value>
public string passTypeIdentifier { get; set; }
/// <summary>
/// Gets or sets the serial number.
/// </summary>
/// <value>
/// The serial number.
/// </value>
public string serialNumber { get; set; }
/// <summary>
/// Gets or sets the team identifier.
/// </summary>
/// <value>
/// The team identifier.
/// </value>
public string teamIdentifier { get; set; }
/// <summary>
/// Gets or sets the name of the organization.
/// </summary>
/// <value>
/// The name of the organization.
/// </value>
public string organizationName { get; set; }
/// <summary>
/// Gets or sets the description.
/// </summary>
/// <value>
/// The description.
/// </value>
public string description { get; set; }
/// <summary>
/// Gets or sets the logo text.
/// </summary>
/// <value>
/// The logo text.
/// </value>
public string logoText { get; set; }
/// <summary>
/// Gets or sets the color of the foreground.
/// </summary>
/// <value>
/// The color of the foreground.
/// </value>
public string foregroundColor { get; set; }
/// <summary>
/// Gets or sets the color of the background.
/// </summary>
/// <value>
/// The color of the background.
/// </value>
public string backgroundColor { get; set; }
/// <summary>
/// Gets or sets the color of the label.
/// </summary>
/// <value>
/// The color of the label.
/// </value>
public string labelColor { get; set; }
/// <summary>
/// Gets or sets the barcode.
/// </summary>
/// <value>
/// The barcode.
/// </value>
public Barcode barcode { get; set; }
}
}
}

Then, although not strictly necessary, this code makes the json much prettier to read;

using System.Collections.Generic;
using System.Text;

namespace libPkPass
{
/// <summary>
/// Optional process, makes the json easier to read
/// </summary>
internal class JsonPrettyPrinter
{
/// <summary>
/// Optional process, makes the json easier to read
/// </summary>
/// <param name=”inputText”>The input text.</param>
/// <returns></returns>
public static string Process(string inputText)
{
var escaped = false;
var inquotes = false;
var column = 0;
var indentation = 0;
var indentations = new Stack<int>();
const int tabbing = 8;
var sb = new StringBuilder();
foreach (var x in inputText)
{
sb.Append(x);
column++;
if (escaped)
{
escaped = false;
}
else
{
switch (x)
{
case ‘\\’:
escaped = true;
break;
case ‘\”‘:
inquotes = !inquotes;
break;
default:
if (!inquotes)
{
switch (x)
{
case ‘,’:
sb.Append(“\r\n”);
column = 0;
for (int i = 0; i < indentation; i++)
{
sb.Append(” “);
column++;
}
break;
case ‘{‘:
case ‘[‘:
indentations.Push(indentation);
indentation = column;
break;
case ‘}’:
case ‘]’:
indentation = indentations.Pop();
break;
case ‘:’:
while ((column%tabbing) != 0)
{
sb.Append(‘ ‘);
column++;
}
break;
}
}
break;
}
}
}
return sb.ToString();
}
}
}

And finally, the magic bit

using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Web.Script.Serialization;
using System.Security.Cryptography.Pkcs;
using Ionic.Zip;

namespace libPkPass
{
/// <summary>
/// Generates .pkpass files
/// </summary>
public class PkPassGenerator
{
/// <summary>
/// Generates the .pkpass.
/// </summary>
/// <param name=”details”>The details.</param>
/// <param name=”assetsFolder”>The assets folder.</param>
/// <param name=”p12File”>The P12 file.</param>
/// <param name=”p12Password”>The P12 password.</param>
public static void GeneratePkPass(JsonObjects.Pass details, string assetsFolder, string p12File, string p12Password)
{
var strPass = JavascriptSerialize(details);
var sw = new StreamWriter(assetsFolder + @”pass.json”);
sw.Write(strPass);
sw.Close();
var manifest = new JsonObjects.Manifest();
// Generate SHA1 hash codes for each of the files required
manifest.strippng = GetSha1Hash(assetsFolder + manifest.GetAsset(JsonObjects.AssetTypes.strippng));
manifest.stripAt2xpng = GetSha1Hash(assetsFolder + manifest.GetAsset(JsonObjects.AssetTypes.stripAt2xpng));
manifest.iconpng = GetSha1Hash(assetsFolder + manifest.GetAsset(JsonObjects.AssetTypes.iconpng));
manifest.iconAt2xpng = GetSha1Hash(assetsFolder + manifest.GetAsset(JsonObjects.AssetTypes.iconAt2xpng));
manifest.logopng = GetSha1Hash(assetsFolder + manifest.GetAsset(JsonObjects.AssetTypes.logopng));
manifest.logoAt2xpng = GetSha1Hash(assetsFolder + manifest.GetAsset(JsonObjects.AssetTypes.logoAt2xpng));
manifest.passjson = GetSha1Hash(assetsFolder + manifest.GetAsset(JsonObjects.AssetTypes.passjson));

// write out manifest.json (adapting variable names)
var strManifest = JavascriptSerialize(manifest);
strManifest = JsonObjects.Manifest.Map.Aggregate(strManifest,
(current, map) => current.Replace(map.property, map.asset));

sw = new StreamWriter(assetsFolder + @”manifest.json”);
sw.Write(strManifest);
sw.Close();

// Generate S/MIME Signature
// http://stackoverflow.com/questions/11526572/openssl-smime-in-c-sharp
var cert = new X509Certificate2(assetsFolder + p12File, p12Password);
var buffer = File.ReadAllBytes(Path.Combine(assetsFolder, “manifest.json”));
var cont = new ContentInfo(buffer);
var cms = new SignedCms(cont, true);
var signer = new CmsSigner(SubjectIdentifierType.SubjectKeyIdentifier, cert)
{
IncludeOption = X509IncludeOption.ExcludeRoot
};
cms.ComputeSignature(signer);
var myCmsMessage = cms.Encode();
File.WriteAllBytes(Path.Combine(assetsFolder, “signature”), myCmsMessage);
using (var zip = new ZipFile())
{
foreach (var asset in JsonObjects.Manifest.Map)
{
zip.AddFile(Path.Combine(assetsFolder, asset.asset), “/”);
}
zip.AddFile(Path.Combine(assetsFolder, “manifest.json”), “/”);
zip.AddFile(Path.Combine(assetsFolder, “signature”), “/”);
zip.Save(Path.Combine(assetsFolder, “pass.pkpass”));
}
}

/// <summary>
/// Converts an object into a JSON string. Works with anonymous types.
/// </summary>
/// <typeparam name=”T”></typeparam>
/// <param name=”o”></param>
/// <returns></returns>
public static string JavascriptSerialize<T>(T o)
{
var jsSerializer = new JavaScriptSerializer();
var strJson = jsSerializer.Serialize(o);
var jsonPretty = JsonPrettyPrinter.Process(strJson);
return jsonPretty;
}

/// <summary>
/// Gets the sha1 hash.
/// </summary>
/// <param name=”sourceFileName”>Name of the source file.</param>
/// <returns></returns>
private static string GetSha1Hash(string sourceFileName)
{
StringBuilder formatted;
using (var fs = new FileStream(sourceFileName, FileMode.Open))
using (var bs = new BufferedStream(fs))
{
using (var sha1 = new SHA1Managed())
{
var hash = sha1.ComputeHash(bs);
formatted = new StringBuilder(2 * hash.Length);
foreach (var b in hash)
{
formatted.AppendFormat(“{0:X2}”, b);
}
}
}
return formatted.ToString().ToLower();
}
}
}

So, how this is used, is something like this:

static void Main(string[] args)
{
// Need to install Apple ROOT cert http://www.apple.com/appleca/AppleIncRootCertificate.cer
const string strAssetsFolder = @”C:\demo\assetsOnly\”;
var pass = new JsonObjects.Pass
{
formatVersion = 1,
passTypeIdentifier = “pass.com.whatever”,
serialNumber = “001”,
teamIdentifier = “XXXXxX”,
organizationName = “Your company”,
description = “Free hug”,
logoText = “Free hugs LLC”,
foregroundColor = “rgb(255, 255, 255)”,
backgroundColor = “rgb(135, 129, 189)”,
labelColor = “rgb(45, 54, 129)”,
barcode = new JsonObjects.Barcode
{
message = “All you need is love”,
format = “PKBarcodeFormatPDF417”,
messageEncoding = “iso-8859-1”
}
};
PkPassGenerator.GeneratePkPass(pass, strAssetsFolder, “pass.p12″,”YYYYY”);
}

Categories: Uncategorized

Creating Apple Passbook #PKPass coupons from Windows

I was following the excellent tutorial by Marin Todorov at http://www.raywenderlich.com/20734/beginning-passbook-part-1 , to create passbook (.pkpass) files from the command line – in order to understand what was going on, rather than depending on third party services to generate them.

However, the tutorial requires a mac, so I adapted it to work from windows as follows;

Download GNUWIN32 http://sourceforge.net/projects/gnuwin32/

—–Generate CSR———-

openssl genrsa -out passbookprivatekey.key 2048

Had to use template openssl.conf (https://www.tbs-certificats.com/openssl-dem-server-cert-thvs.cnf)

set OPENSSL_CONF=%cd%\openssl.cnf

openssl req -new -key passbookprivatekey.key -out PassbookCSR.certSigningRequest -subj “/emailAddress=you@domain.com, CN=Company, C=GB”

> send to apple, and get pass.cer back.

—–Convert CER to P12—–

openssl x509 -inform der -in pass.cer -out pass.pem

openssl pkcs12 -export -out pass.p12 -inkey passbookprivatekey.key -in pass.pem

password XXXX
—- And finally, create signature —-

openssl pkcs12 -in pass.p12 -clcerts -nokeys -out passcertificate.pem -passin pass:XXXX

openssl pkcs12 -in pass.p12 -nocerts -out passkey.pem -passin pass:XXXX -passout pass:XXXX

Download Apple AppleWWDRCA.cer

openssl x509 -inform der -in AppleWWDRCA.cer -out AppleWWDRCA.pem

openssl x509 -inform der -in pass.cer -out pass.pem

openssl smime -binary -sign -certfile AppleWWDRCA.pem -signer passcertificate.pem -inkey passkey.pem
-in manifest.json -out signature -outform DER -passin pass:XXXX

Categories: Uncategorized

#iOS9 #Breaking #Change – HTTP connections blocked by default!

In the new iOS 9 Apple has decided that HTTP is too insecure for communications outside of your app… If you try to make one, you get this error:

Failed to load resource: The resource could not be loaded because the App Transport Security policy requires the use of a secure connection.

So, you can go and get a HTTPS security cert for your server – perhaps the best option – or you can add this to your info.plist

<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>

I discovered this when using http://www.remotelogcat.com on a Phonegap / Cordova iOS app.

Categories: Uncategorized

Understanding the basics of SPF records #EmailTech

SPF or “Sender Policy Framework” is a technique that aims to prevent email sender spoofing, and ultimately cut spam, it means that senders without a properly configured SPF record may be considered spam.

But if your business sends out email, and you want to make sure it isn’t marked as spam at your destination, then you might need to look at your SPF records.

The first step is to look at the headers of a received email, by a client that handles SPF, i.e. Gmail, and if you see something like spf=softfail ( domain of transitioning does not designate as permitted sender) , this means SPF is not configured correctly.

So, lets say, you know the sender is going to have IP address 1.2.3.4 – i.e. your outbound SMTP server. and your sender email is whatever@me.com – you need to then add a DNS TXT record to the domain me.com with the text

v=spf1 ip4:1.2.3.4 ~all

For the host “@” (non-www)

Then try sending the email again, and view the headers on the message, and it should say

Received-SPF: pass (google.com: domain of whatever@me.com designates 1.2.3.4 as permitted sender) client-ip=1.2.3.4;

This will come into play big time if you use Direct to MX software, or Amazon SES

Categories: Uncategorized

Google Chrome uses code written by Apple!

Open developer tools, click console, type a non-existent variable – then click on

Uncaught ReferenceError: sdgdsg is not defined
at <anonymous>:2:1
at Object.InjectedScript._evaluateOn (<anonymous>:905:140)
at Object.InjectedScript._evaluateAndWrap (<anonymous>:838:34)
at Object.InjectedScript.evaluate (<anonymous>:694:21)

then the @ VM links

and you get;

/*
* Copyright (C) 2007 Apple Inc. All rights reserved.
* Copyright (C) 2013 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. (“Apple”) nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS “AS IS” AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

“use strict”;

/**
* @param {!InjectedScriptHostClass} InjectedScriptHost
* @param {!Window} inspectedWindow
* @param {number} injectedScriptId
*/
(function (InjectedScriptHost, inspectedWindow, injectedScriptId) {

/**
* Protect against Object overwritten by the user code.
* @suppress {duplicate}
*/
var Object = /** @type {function(new:Object, *=)} */ ({}.constructor);

/**
* @param {!Array.<T>} array
* @param {…} var_args
* @template T
*/
function push(array, var_args)
{
for (var i = 1; i < arguments.length; ++i)
array[array.length] = arguments[i];
}

/**
* @param {!Arguments.<T>} array
* @param {number=} index
* @return {!Array.<T>}
* @template T
*/
function slice(array, index)
{
var result = [];
for (var i = index || 0, j = 0; i < array.length; ++i, ++j)
result[j] = array[i];
return result;
}

/**
* @param {!Array.<T>} array1
* @param {!Array.<T>} array2
* @return {!Array.<T>}
* @template T
*/
function concat(array1, array2)
{
var result = [];
for (var i = 0; i < array1.length; ++i)
push(result, array1[i]);
for (var i = 0; i < array2.length; ++i)
push(result, array2[i]);
return result;
}

/**
* @param {*} obj
* @return {string}
* @suppress {uselessCode}
*/
function toString(obj)
{
// We don’t use String(obj) because String could be overridden.
// Also the (“” + obj) expression may throw.
try {
return “” + obj;
} catch (e) {
var name = InjectedScriptHost.internalConstructorName(obj) || InjectedScriptHost.subtype(obj) || (typeof obj);
return “#<” + name + “>”;
}
}

/**
* @param {*} obj
* @return {string}
*/
function toStringDescription(obj)
{
if (typeof obj === “number” && obj === 0 && 1 / obj < 0)
return “-0”; // Negative zero.
return toString(obj);
}

/**
* Please use this bind, not the one from Function.prototype
* @param {function(…)} func
* @param {?Object} thisObject
* @param {…} var_args
* @return {function(…)}
*/
function bind(func, thisObject, var_args)
{
var args = slice(arguments, 2);

/**
* @param {…} var_args
*/
function bound(var_args)
{
return InjectedScriptHost.callFunction(func, thisObject, concat(args, slice(arguments)));
}
bound.toString = function()
{
return “bound: ” + toString(func);
};
return bound;
}

/**
* @param {T} obj
* @return {T}
* @template T
*/
function nullifyObjectProto(obj)
{
if (obj && typeof obj === “object”)
obj.__proto__ = null;
return obj;
}

/**
* @param {number|string} obj
* @return {boolean}
*/
function isUInt32(obj)
{
if (typeof obj === “number”)
return obj >>> 0 === obj && (obj > 0 || 1 / obj > 0);
return “” + (obj >>> 0) === obj;
}

/**
* FireBug’s array detection.
* @param {*} obj
* @return {boolean}
*/
function isArrayLike(obj)
{
if (typeof obj !== “object”)
return false;
try {
if (typeof obj.splice === “function”) {
var len = obj.length;
return typeof len === “number” && isUInt32(len);
}
} catch (e) {
}
return false;
}

/**
* @param {number} a
* @param {number} b
* @return {number}
*/
function max(a, b)
{
return a > b ? a : b;
}

/**
* FIXME: Remove once ES6 is supported natively by JS compiler.
* @param {*} obj
* @return {boolean}
*/
function isSymbol(obj)
{
var type = typeof obj;
return (type === “symbol”);
}

/**
* @param {string} str
* @param {string} searchElement
* @param {number=} fromIndex
* @return {number}
*/
function indexOf(str, searchElement, fromIndex)
{
var len = str.length;
var n = fromIndex || 0;
var k = max(n >= 0 ? n : len + n, 0);

while (k < len) {
if (str[k] === searchElement)
return k;
++k;
}
return -1;
}

/**
* DOM Attributes which have observable side effect on getter, in the form of
* {interfaceName1: {attributeName1: true,
* attributeName2: true,
* …},
* interfaceName2: {…},
* …}
* @type {!Object<string, !Object<string, boolean>>}
* @const
*/
var domAttributesWithObservableSideEffectOnGet = nullifyObjectProto({});
domAttributesWithObservableSideEffectOnGet[“Request”] = nullifyObjectProto({});
domAttributesWithObservableSideEffectOnGet[“Request”][“body”] = true;
domAttributesWithObservableSideEffectOnGet[“Response”] = nullifyObjectProto({});
domAttributesWithObservableSideEffectOnGet[“Response”][“body”] = true;

/**
* @param {!Object} object
* @param {string} attribute
* @return {boolean}
*/
function doesAttributeHaveObservableSideEffectOnGet(object, attribute)
{
for (var interfaceName in domAttributesWithObservableSideEffectOnGet) {
var isInstance = InjectedScriptHost.suppressWarningsAndCallFunction(function(object, interfaceName) {
return /* suppressBlacklist */ typeof inspectedWindow[interfaceName] === “function” && object instanceof inspectedWindow[interfaceName];
}, null, [object, interfaceName]);
if (isInstance) {
return attribute in domAttributesWithObservableSideEffectOnGet[interfaceName];
}
}
return false;
}

/**
* @constructor
*/
var InjectedScript = function()
{
/** @type {!Object.<string, !Object>} */
this._modules = { __proto__: null };
}

/**
* @type {!Object.<string, boolean>}
* @const
*/
InjectedScript.primitiveTypes = {
“undefined”: true,
“boolean”: true,
“number”: true,
“string”: true,
__proto__: null
}

InjectedScript.prototype = {
/**
* @param {*} object
* @return {boolean}
*/
isPrimitiveValue: function(object)
{
// FIXME(33716): typeof document.all is always ‘undefined’.
return InjectedScript.primitiveTypes[typeof object] && !this._isHTMLAllCollection(object);
},

/**
* @param {*} object
* @param {string} groupName
* @param {boolean} canAccessInspectedWindow
* @param {boolean} generatePreview
* @return {!RuntimeAgent.RemoteObject}
*/
wrapObject: function(object, groupName, canAccessInspectedWindow, generatePreview)
{
if (canAccessInspectedWindow)
return this._wrapObject(object, groupName, false, generatePreview);
return this._fallbackWrapper(object);
},

/**
* @param {*} object
* @param {string} groupName
* @param {boolean=} doNotBind
* @return {!RuntimeAgent.RemoteObject}
*/
wrapObjectForModule: function(object, groupName, doNotBind)
{
return this._wrapObject(object, groupName, false, false, null, false, doNotBind);
},

/**
* @param {*} object
* @return {!RuntimeAgent.RemoteObject}
*/
_fallbackWrapper: function(object)
{
var result = { __proto__: null };
result.type = typeof object;
if (this.isPrimitiveValue(object))
result.value = object;
else
result.description = toString(object);
return /** @type {!RuntimeAgent.RemoteObject} */ (result);
},

/**
* @param {boolean} canAccessInspectedWindow
* @param {!Object} table
* @param {!Array.<string>|string|boolean} columns
* @return {!RuntimeAgent.RemoteObject}
*/
wrapTable: function(canAccessInspectedWindow, table, columns)
{
if (!canAccessInspectedWindow)
return this._fallbackWrapper(table);
var columnNames = null;
if (typeof columns === “string”)
columns = [columns];
if (InjectedScriptHost.subtype(columns) === “array”) {
columnNames = [];
for (var i = 0; i < columns.length; ++i)
columnNames[i] = toString(columns[i]);
}
return this._wrapObject(table, “console”, false, true, columnNames, true);
},

/**
* @param {*} object
* @return {*}
*/
_inspect: function(object)
{
if (arguments.length === 0)
return;

var objectId = this._wrapObject(object, “”);
var hints = { __proto__: null };

InjectedScriptHost.inspect(objectId, hints);
return object;
},

/**
* This method cannot throw.
* @param {*} object
* @param {string=} objectGroupName
* @param {boolean=} forceValueType
* @param {boolean=} generatePreview
* @param {?Array.<string>=} columnNames
* @param {boolean=} isTable
* @param {boolean=} doNotBind
* @param {*=} customObjectConfig
* @return {!RuntimeAgent.RemoteObject}
* @suppress {checkTypes}
*/
_wrapObject: function(object, objectGroupName, forceValueType, generatePreview, columnNames, isTable, doNotBind, customObjectConfig)
{
try {
return new InjectedScript.RemoteObject(object, objectGroupName, doNotBind, forceValueType, generatePreview, columnNames, isTable, undefined, customObjectConfig);
} catch (e) {
try {
var description = injectedScript._describe(e);
} catch (ex) {
var description = “<failed to convert exception to string>”;
}
return new InjectedScript.RemoteObject(description);
}
},

/**
* @param {!Object|symbol} object
* @param {string=} objectGroupName
* @return {string}
*/
_bind: function(object, objectGroupName)
{
var id = InjectedScriptHost.bind(object, objectGroupName || “”);
return “{\”injectedScriptId\”:” + injectedScriptId + “,\”id\”:” + id + “}”;
},

/**
* @param {string} objectId
* @return {!Object}
*/
_parseObjectId: function(objectId)
{
return nullifyObjectProto(/** @type {!Object} */ (InjectedScriptHost.eval(“(” + objectId + “)”)));
},

clearLastEvaluationResult: function()
{
delete this._lastResult;
},

/**
* @param {string} methodName
* @param {string} args
* @return {*}
*/
dispatch: function(methodName, args)
{
var argsArray = /** @type {!Array.<*>} */ (InjectedScriptHost.eval(“(” + args + “)”));
var result = InjectedScriptHost.callFunction(this[methodName], this, argsArray);
if (typeof result === “undefined”) {
inspectedWindow.console.error(“Web Inspector error: InjectedScript.%s returns undefined”, methodName);
result = null;
}
return result;
},

/**
* @param {string} objectId
* @param {boolean} ownProperties
* @param {boolean} accessorPropertiesOnly
* @param {boolean} generatePreview
* @return {!Array.<!RuntimeAgent.PropertyDescriptor>|boolean}
*/
getProperties: function(objectId, ownProperties, accessorPropertiesOnly, generatePreview)
{
var parsedObjectId = this._parseObjectId(objectId);
var object = this._objectForId(parsedObjectId);
var objectGroupName = InjectedScriptHost.idToObjectGroupName(parsedObjectId.id);

if (!this._isDefined(object) || isSymbol(object))
return false;
object = /** @type {!Object} */ (object);
var descriptors = [];
var iter = this._propertyDescriptors(object, ownProperties, accessorPropertiesOnly);
// Go over properties, wrap object values.
for (var descriptor of iter) {
if (“get” in descriptor)
descriptor.get = this._wrapObject(descriptor.get, objectGroupName);
if (“set” in descriptor)
descriptor.set = this._wrapObject(descriptor.set, objectGroupName);
if (“value” in descriptor)
descriptor.value = this._wrapObject(descriptor.value, objectGroupName, false, generatePreview);
if (!(“configurable” in descriptor))
descriptor.configurable = false;
if (!(“enumerable” in descriptor))
descriptor.enumerable = false;
if (“symbol” in descriptor)
descriptor.symbol = this._wrapObject(descriptor.symbol, objectGroupName);
push(descriptors, descriptor);
}
return descriptors;
},

/**
* @param {string} objectId
* @return {!Array.<!Object>|boolean}
*/
getInternalProperties: function(objectId)
{
var parsedObjectId = this._parseObjectId(objectId);
var object = this._objectForId(parsedObjectId);
var objectGroupName = InjectedScriptHost.idToObjectGroupName(parsedObjectId.id);
if (!this._isDefined(object) || isSymbol(object))
return false;
object = /** @type {!Object} */ (object);
var descriptors = [];
var internalProperties = InjectedScriptHost.getInternalProperties(object);
if (internalProperties) {
for (var i = 0; i < internalProperties.length; i++) {
var property = internalProperties[i];
var descriptor = {
name: property.name,
value: this._wrapObject(property.value, objectGroupName),
__proto__: null
};
push(descriptors, descriptor);
}
}
return descriptors;
},

/**
* @param {string} functionId
* @return {!DebuggerAgent.FunctionDetails|string}
*/
getFunctionDetails: function(functionId)
{
var parsedFunctionId = this._parseObjectId(functionId);
var func = this._objectForId(parsedFunctionId);
if (typeof func !== “function”)
return “Cannot resolve function by id.”;
var details = nullifyObjectProto(/** @type {!DebuggerAgent.FunctionDetails} */ (InjectedScriptHost.functionDetails(func)));
if (“rawScopes” in details) {
var objectGroupName = InjectedScriptHost.idToObjectGroupName(parsedFunctionId.id);
var rawScopes = details[“rawScopes”];
delete details[“rawScopes”];
var scopes = [];
for (var i = 0; i < rawScopes.length; ++i)
scopes[i] = InjectedScript.CallFrameProxy._createScopeJson(rawScopes[i].type, rawScopes[i].object, objectGroupName);
details.scopeChain = scopes;
}
return details;
},

/**
* @param {string} objectId
* @return {!DebuggerAgent.GeneratorObjectDetails|string}
*/
getGeneratorObjectDetails: function(objectId)
{
var parsedObjectId = this._parseObjectId(objectId);
var object = this._objectForId(parsedObjectId);
if (!object || typeof object !== “object”)
return “Could not find object with given id”;
var details = nullifyObjectProto(/** @type {?DebuggerAgent.GeneratorObjectDetails} */ (InjectedScriptHost.generatorObjectDetails(object)));
if (!details)
return “Object is not a generator”;
var objectGroupName = InjectedScriptHost.idToObjectGroupName(parsedObjectId.id);
details[“function”] = this._wrapObject(details[“function”], objectGroupName);
return details;
},

/**
* @param {string} objectId
* @return {!Array.<!Object>|string}
*/
getCollectionEntries: function(objectId)
{
var parsedObjectId = this._parseObjectId(objectId);
var object = this._objectForId(parsedObjectId);
if (!object || typeof object !== “object”)
return “Could not find object with given id”;
var entries = InjectedScriptHost.collectionEntries(object);
if (!entries)
return “Object with given id is not a collection”;
var objectGroupName = InjectedScriptHost.idToObjectGroupName(parsedObjectId.id);
for (var i = 0; i < entries.length; ++i) {
var entry = nullifyObjectProto(entries[i]);
if (“key” in entry)
entry.key = this._wrapObject(entry.key, objectGroupName);
entry.value = this._wrapObject(entry.value, objectGroupName);
entries[i] = entry;
}
return entries;
},

/**
* @param {!Object} object
* @param {boolean=} ownProperties
* @param {boolean=} accessorPropertiesOnly
* @param {?Array.<string>=} propertyNamesOnly
*/
_propertyDescriptors: function*(object, ownProperties, accessorPropertiesOnly, propertyNamesOnly)
{
var propertyProcessed = { __proto__: null };

/**
* @param {?Object} o
* @param {!Iterable.<string|symbol>|!Array.<string|symbol>} properties
*/
function* process(o, properties)
{
for (var property of properties) {
if (propertyProcessed[property])
continue;

var name = property;
if (isSymbol(property))
name = /** @type {string} */ (injectedScript._describe(property));

try {
propertyProcessed[property] = true;
var descriptor = nullifyObjectProto(InjectedScriptHost.suppressWarningsAndCallFunction(Object.getOwnPropertyDescriptor, Object, [o, property]));
if (descriptor) {
if (accessorPropertiesOnly && !(“get” in descriptor || “set” in descriptor))
continue;
if (“get” in descriptor && “set” in descriptor && name != “__proto__” && InjectedScriptHost.isDOMWrapper(object) && !doesAttributeHaveObservableSideEffectOnGet(object, name)) {
descriptor.value = InjectedScriptHost.suppressWarningsAndCallFunction(function(attribute) { return this[attribute]; }, object, [name]);
delete descriptor.get;
delete descriptor.set;
}
} else {
// Not all bindings provide proper descriptors. Fall back to the writable, configurable property.
if (accessorPropertiesOnly)
continue;
try {
descriptor = { name: name, value: o[property], writable: false, configurable: false, enumerable: false, __proto__: null };
if (o === object)
descriptor.isOwn = true;
yield descriptor;
} catch (e) {
// Silent catch.
}
continue;
}
} catch (e) {
if (accessorPropertiesOnly)
continue;
var descriptor = { __proto__: null };
descriptor.value = e;
descriptor.wasThrown = true;
}

descriptor.name = name;
if (o === object)
descriptor.isOwn = true;
if (isSymbol(property))
descriptor.symbol = property;
yield descriptor;
}
}

/**
* @param {number} length
*/
function* arrayIndexNames(length)
{
for (var i = 0; i < length; ++i)
yield “” + i;
}

if (propertyNamesOnly) {
for (var i = 0; i < propertyNamesOnly.length; ++i) {
var name = propertyNamesOnly[i];
for (var o = object; this._isDefined(o); o = o.__proto__) {
if (InjectedScriptHost.suppressWarningsAndCallFunction(Object.prototype.hasOwnProperty, o, [name])) {
for (var descriptor of process(o, [name]))
yield descriptor;
break;
}
if (ownProperties)
break;
}
}
return;
}

var skipGetOwnPropertyNames;
try {
skipGetOwnPropertyNames = InjectedScriptHost.isTypedArray(object) && object.length > 500000;
} catch (e) {
}

for (var o = object; this._isDefined(o); o = o.__proto__) {
if (skipGetOwnPropertyNames && o === object) {
// Avoid OOM crashes from getting all own property names of a large TypedArray.
for (var descriptor of process(o, arrayIndexNames(o.length)))
yield descriptor;
} else {
// First call Object.keys() to enforce ordering of the property descriptors.
for (var descriptor of process(o, Object.keys(/** @type {!Object} */ (o))))
yield descriptor;
for (var descriptor of process(o, Object.getOwnPropertyNames(/** @type {!Object} */ (o))))
yield descriptor;
}
if (Object.getOwnPropertySymbols) {
for (var descriptor of process(o, Object.getOwnPropertySymbols(/** @type {!Object} */ (o))))
yield descriptor;
}
if (ownProperties) {
if (object.__proto__ && !accessorPropertiesOnly)
yield { name: “__proto__”, value: object.__proto__, writable: true, configurable: true, enumerable: false, isOwn: true, __proto__: null };
break;
}
}
},

/**
* @param {string} expression
* @param {string} objectGroup
* @param {boolean} injectCommandLineAPI
* @param {boolean} returnByValue
* @param {boolean} generatePreview
* @return {*}
*/
evaluate: function(expression, objectGroup, injectCommandLineAPI, returnByValue, generatePreview)
{
return this._evaluateAndWrap(null, expression, objectGroup, injectCommandLineAPI, returnByValue, generatePreview);
},

/**
* @param {string} objectId
* @param {string} expression
* @param {string} args
* @param {boolean} returnByValue
* @return {!Object|string}
*/
callFunctionOn: function(objectId, expression, args, returnByValue)
{
var parsedObjectId = this._parseObjectId(objectId);
var object = this._objectForId(parsedObjectId);
if (!this._isDefined(object))
return “Could not find object with given id”;

if (args) {
var resolvedArgs = [];
var callArgs = /** @type {!Array.<!RuntimeAgent.CallArgument>} */ (InjectedScriptHost.eval(args));
for (var i = 0; i < callArgs.length; ++i) {
try {
resolvedArgs[i] = this._resolveCallArgument(callArgs[i]);
} catch (e) {
return toString(e);
}
}
}

var objectGroup = InjectedScriptHost.idToObjectGroupName(parsedObjectId.id);

/**
* @suppressReceiverCheck
* @param {*} object
* @param {boolean=} forceValueType
* @param {boolean=} generatePreview
* @param {?Array.<string>=} columnNames
* @param {boolean=} isTable
* @param {*=} customObjectConfig
* @return {!RuntimeAgent.RemoteObject}
* @this {InjectedScript}
*/
function wrap(object, forceValueType, generatePreview, columnNames, isTable, customObjectConfig)
{
return this._wrapObject(object, objectGroup, forceValueType, generatePreview, columnNames, isTable, false, customObjectConfig);
}

try {

var remoteObjectAPI = { bindRemoteObject: bind(wrap, this), __proto__: null};
InjectedScriptHost.setNonEnumProperty(inspectedWindow, “__remoteObjectAPI”, remoteObjectAPI);

var func = InjectedScriptHost.eval(“with (typeof __remoteObjectAPI !== ‘undefined’ ? __remoteObjectAPI : { __proto__: null }) {(” + expression + “)}”);
if (typeof func !== “function”)
return “Given expression does not evaluate to a function”;

return { wasThrown: false,
result: this._wrapObject(InjectedScriptHost.callFunction(func, object, resolvedArgs), objectGroup, returnByValue),
__proto__: null };
} catch (e) {
return this._createThrownValue(e, objectGroup, false);
} finally {
try {
delete inspectedWindow[“__remoteObjectAPI”];
} catch(e) {
}
}
},

/**
* @param {string|undefined} objectGroupName
* @param {*} jsonMLObject
* @throws {string} error message
*/
_substituteObjectTagsInCustomPreview: function(objectGroupName, jsonMLObject)
{
var maxCustomPreviewRecursionDepth = 20;
this._customPreviewRecursionDepth = (this._customPreviewRecursionDepth || 0) + 1
try {
if (this._customPreviewRecursionDepth >= maxCustomPreviewRecursionDepth)
throw new Error(“Too deep hierarchy of inlined custom previews”);

if (!isArrayLike(jsonMLObject))
return;

if (jsonMLObject[0] === “object”) {
var attributes = jsonMLObject[1];
var originObject = attributes[“object”];
var config = attributes[“config”];
if (typeof originObject === “undefined”)
throw new Error(“Illegal format: obligatory attribute \”object\” isn’t specified”);

jsonMLObject[1] = this._wrapObject(originObject, objectGroupName, false, false, null, false, false, config);
return;
}

for (var i = 0; i < jsonMLObject.length; ++i)
this._substituteObjectTagsInCustomPreview(objectGroupName, jsonMLObject[i]);
} finally {
this._customPreviewRecursionDepth–;
}
},

/**
* Resolves a value from CallArgument description.
* @param {!RuntimeAgent.CallArgument} callArgumentJson
* @return {*} resolved value
* @throws {string} error message
*/
_resolveCallArgument: function(callArgumentJson)
{
callArgumentJson = nullifyObjectProto(callArgumentJson);
var objectId = callArgumentJson.objectId;
if (objectId) {
var parsedArgId = this._parseObjectId(objectId);
if (!parsedArgId || parsedArgId[“injectedScriptId”] !== injectedScriptId)
throw “Arguments should belong to the same JavaScript world as the target object.”;

var resolvedArg = this._objectForId(parsedArgId);
if (!this._isDefined(resolvedArg))
throw “Could not find object with given id”;

return resolvedArg;
} else if (“value” in callArgumentJson) {
var value = callArgumentJson.value;
if (callArgumentJson.type === “number” && typeof value !== “number”)
value = Number(value);
return value;
}
return undefined;
},

/**
* @param {?JavaScriptCallFrame} callFrame
* @param {string} expression
* @param {string} objectGroup
* @param {boolean} injectCommandLineAPI
* @param {boolean} returnByValue
* @param {boolean} generatePreview
* @param {!Array.<!Object>=} scopeChain
* @return {!Object}
*/
_evaluateAndWrap: function(callFrame, expression, objectGroup, injectCommandLineAPI, returnByValue, generatePreview, scopeChain)
{
var wrappedResult = this._evaluateOn(callFrame, objectGroup, expression, injectCommandLineAPI, scopeChain);
if (!wrappedResult.exceptionDetails) {
return { wasThrown: false,
result: this._wrapObject(wrappedResult.result, objectGroup, returnByValue, generatePreview),
__proto__: null };
}
return this._createThrownValue(wrappedResult.result, objectGroup, generatePreview, wrappedResult.exceptionDetails);
},

/**
* @param {*} value
* @param {string|undefined} objectGroup
* @param {boolean} generatePreview
* @param {!DebuggerAgent.ExceptionDetails=} exceptionDetails
* @return {!Object}
*/
_createThrownValue: function(value, objectGroup, generatePreview, exceptionDetails)
{
var remoteObject = this._wrapObject(value, objectGroup, false, generatePreview && InjectedScriptHost.subtype(value) !== “error”);
if (!remoteObject.description){
try {
remoteObject.description = toStringDescription(value);
} catch (e) {}
}
return { wasThrown: true, result: remoteObject, exceptionDetails: exceptionDetails, __proto__: null };
},

/**
* @param {?JavaScriptCallFrame} callFrame
* @param {string} objectGroup
* @param {string} expression
* @param {boolean} injectCommandLineAPI
* @param {!Array.<!Object>=} scopeChain
* @return {*}
*/
_evaluateOn: function(callFrame, objectGroup, expression, injectCommandLineAPI, scopeChain)
{
// Only install command line api object for the time of evaluation.
// Surround the expression in with statements to inject our command line API so that
// the window object properties still take more precedent than our API functions.

var scopeExtensionForEval = (callFrame && injectCommandLineAPI) ? new CommandLineAPI(this._commandLineAPIImpl, callFrame) : undefined;

injectCommandLineAPI = !scopeExtensionForEval && !callFrame && injectCommandLineAPI && !(“__commandLineAPI” in inspectedWindow);
var injectScopeChain = scopeChain && scopeChain.length && !(“__scopeChainForEval” in inspectedWindow);

try {
var prefix = “”;
var suffix = “”;
if (injectCommandLineAPI) {
InjectedScriptHost.setNonEnumProperty(inspectedWindow, “__commandLineAPI”, new CommandLineAPI(this._commandLineAPIImpl, callFrame));
prefix = “with (typeof __commandLineAPI !== ‘undefined’ ? __commandLineAPI : { __proto__: null }) {“;
suffix = “}”;
}
if (injectScopeChain) {
InjectedScriptHost.setNonEnumProperty(inspectedWindow, “__scopeChainForEval”, scopeChain);
for (var i = 0; i < scopeChain.length; ++i) {
prefix = “with (typeof __scopeChainForEval !== ‘undefined’ ? __scopeChainForEval[” + i + “] : { __proto__: null }) {” + (suffix ? ” ” : “”) + prefix;
if (suffix)
suffix += ” }”;
else
suffix = “}”;
}
}

if (prefix)
expression = prefix + “\n” + expression + “\n” + suffix;
var wrappedResult = callFrame ? callFrame.evaluateWithExceptionDetails(expression, scopeExtensionForEval) : InjectedScriptHost.evaluateWithExceptionDetails(expression);
if (objectGroup === “console” && !wrappedResult.exceptionDetails)
this._lastResult = wrappedResult.result;
return wrappedResult;
} finally {
if (injectCommandLineAPI) {
try {
delete inspectedWindow[“__commandLineAPI”];
} catch(e) {
}
}
if (injectScopeChain) {
try {
delete inspectedWindow[“__scopeChainForEval”];
} catch(e) {
}
}
}
},

/**
* @param {?Object} callFrame
* @param {number} asyncOrdinal
* @return {!Array.<!InjectedScript.CallFrameProxy>|boolean}
*/
wrapCallFrames: function(callFrame, asyncOrdinal)
{
if (!callFrame)
return false;

var result = [];
var depth = 0;
do {
result[depth] = new InjectedScript.CallFrameProxy(depth, callFrame, asyncOrdinal);
callFrame = callFrame.caller;
++depth;
} while (callFrame);
return result;
},

/**
* @param {!JavaScriptCallFrame} topCallFrame
* @param {!Array.<!JavaScriptCallFrame>} asyncCallStacks
* @param {string} callFrameId
* @param {string} expression
* @param {string} objectGroup
* @param {boolean} injectCommandLineAPI
* @param {boolean} returnByValue
* @param {boolean} generatePreview
* @return {*}
*/
evaluateOnCallFrame: function(topCallFrame, asyncCallStacks, callFrameId, expression, objectGroup, injectCommandLineAPI, returnByValue, generatePreview)
{
var parsedCallFrameId = nullifyObjectProto(/** @type {!Object} */ (InjectedScriptHost.eval(“(” + callFrameId + “)”)));
var callFrame = this._callFrameForParsedId(topCallFrame, parsedCallFrameId, asyncCallStacks);
if (!callFrame)
return “Could not find call frame with given id”;
if (parsedCallFrameId[“asyncOrdinal”])
return this._evaluateAndWrap(null, expression, objectGroup, injectCommandLineAPI, returnByValue, generatePreview, callFrame.scopeChain);
return this._evaluateAndWrap(callFrame, expression, objectGroup, injectCommandLineAPI, returnByValue, generatePreview);
},

/**
* @param {!JavaScriptCallFrame} topCallFrame
* @param {string} callFrameId
* @return {*}
*/
restartFrame: function(topCallFrame, callFrameId)
{
var callFrame = this._callFrameForId(topCallFrame, callFrameId);
if (!callFrame)
return “Could not find call frame with given id”;
var result = callFrame.restart();
if (result === false)
result = “Restart frame is not supported”;
return result;
},

/**
* @param {!JavaScriptCallFrame} topCallFrame
* @param {string} callFrameId
* @return {*} a stepIn position array ready for protocol JSON or a string error
*/
getStepInPositions: function(topCallFrame, callFrameId)
{
var callFrame = this._callFrameForId(topCallFrame, callFrameId);
if (!callFrame)
return “Could not find call frame with given id”;
var stepInPositionsUnpacked = JSON.parse(callFrame.stepInPositions);
if (typeof stepInPositionsUnpacked !== “object”)
return “Step in positions not available”;
return stepInPositionsUnpacked;
},

/**
* Either callFrameId or functionObjectId must be specified.
* @param {!JavaScriptCallFrame} topCallFrame
* @param {string|boolean} callFrameId or false
* @param {string|boolean} functionObjectId or false
* @param {number} scopeNumber
* @param {string} variableName
* @param {string} newValueJsonString RuntimeAgent.CallArgument structure serialized as string
* @return {string|undefined} undefined if success or an error message
*/
setVariableValue: function(topCallFrame, callFrameId, functionObjectId, scopeNumber, variableName, newValueJsonString)
{
try {
var newValueJson = /** @type {!RuntimeAgent.CallArgument} */ (InjectedScriptHost.eval(“(” + newValueJsonString + “)”));
var resolvedValue = this._resolveCallArgument(newValueJson);
if (typeof callFrameId === “string”) {
var callFrame = this._callFrameForId(topCallFrame, callFrameId);
if (!callFrame)
return “Could not find call frame with given id”;
callFrame.setVariableValue(scopeNumber, variableName, resolvedValue)
} else {
var parsedFunctionId = this._parseObjectId(/** @type {string} */ (functionObjectId));
var func = this._objectForId(parsedFunctionId);
if (typeof func !== “function”)
return “Could not resolve function by id”;
InjectedScriptHost.setFunctionVariableValue(func, scopeNumber, variableName, resolvedValue);
}
} catch (e) {
return toString(e);
}
return undefined;
},

/**
* @param {!JavaScriptCallFrame} topCallFrame
* @param {string} callFrameId
* @return {?JavaScriptCallFrame}
*/
_callFrameForId: function(topCallFrame, callFrameId)
{
var parsedCallFrameId = nullifyObjectProto(/** @type {!Object} */ (InjectedScriptHost.eval(“(” + callFrameId + “)”)));
return this._callFrameForParsedId(topCallFrame, parsedCallFrameId, []);
},

/**
* @param {!JavaScriptCallFrame} topCallFrame
* @param {!Object} parsedCallFrameId
* @param {!Array.<!JavaScriptCallFrame>} asyncCallStacks
* @return {?JavaScriptCallFrame}
*/
_callFrameForParsedId: function(topCallFrame, parsedCallFrameId, asyncCallStacks)
{
var asyncOrdinal = parsedCallFrameId[“asyncOrdinal”]; // 1-based index
if (asyncOrdinal)
topCallFrame = asyncCallStacks[asyncOrdinal – 1];
var ordinal = parsedCallFrameId[“ordinal”];
var callFrame = topCallFrame;
while (–ordinal >= 0 && callFrame)
callFrame = callFrame.caller;
return callFrame;
},

/**
* @param {!Object} objectId
* @return {!Object|symbol|undefined}
*/
_objectForId: function(objectId)
{
return objectId.injectedScriptId === injectedScriptId ? /** @type{!Object|symbol|undefined} */ (InjectedScriptHost.objectForId(objectId.id)) : void 0;
},

/**
* @param {string} objectId
* @return {!Object|symbol|undefined}
*/
findObjectById: function(objectId)
{
var parsedObjectId = this._parseObjectId(objectId);
return this._objectForId(parsedObjectId);
},

/**
* @param {string} objectId
* @return {?Node}
*/
nodeForObjectId: function(objectId)
{
var object = this.findObjectById(objectId);
if (!object || this._subtype(object) !== “node”)
return null;
return /** @type {!Node} */ (object);
},

/**
* @param {string} name
* @return {!Object}
*/
module: function(name)
{
return this._modules[name];
},

/**
* @param {string} name
* @param {string} source
* @return {?Object}
*/
injectModule: function(name, source)
{
delete this._modules[name];
var moduleFunction = InjectedScriptHost.eval(“(” + source + “)”);
if (typeof moduleFunction !== “function”) {
inspectedWindow.console.error(“Web Inspector error: A function was expected for module %s evaluation”, name);
return null;
}
var module = /** @type {!Object} */ (InjectedScriptHost.callFunction(moduleFunction, inspectedWindow, [InjectedScriptHost, inspectedWindow, injectedScriptId, this]));
this._modules[name] = module;
return module;
},

/**
* @param {*} object
* @return {boolean}
*/
_isDefined: function(object)
{
return !!object || this._isHTMLAllCollection(object);
},

/**
* @param {*} object
* @return {boolean}
*/
_isHTMLAllCollection: function(object)
{
// document.all is reported as undefined, but we still want to process it.
return (typeof object === “undefined”) && InjectedScriptHost.isHTMLAllCollection(object);
},

/**
* @param {*} obj
* @return {?string}
*/
_subtype: function(obj)
{
if (obj === null)
return “null”;

if (this.isPrimitiveValue(obj))
return null;

var subtype = InjectedScriptHost.subtype(obj);
if (subtype)
return subtype;

if (isArrayLike(obj))
return “array”;

// If owning frame has navigated to somewhere else window properties will be undefined.
return null;
},

/**
* @param {*} obj
* @return {?string}
*/
_describe: function(obj)
{
if (this.isPrimitiveValue(obj))
return null;

var subtype = this._subtype(obj);

if (subtype === “regexp”)
return toString(obj);

if (subtype === “date”)
return toString(obj);

if (subtype === “node”) {
var description = obj.nodeName.toLowerCase();
switch (obj.nodeType) {
case 1 /* Node.ELEMENT_NODE */:
description += obj.id ? “#” + obj.id : “”;
var className = obj.className;
description += (className && typeof className === “string”) ? “.” + className.trim().replace(/\s+/g, “.”) : “”;
break;
case 10 /*Node.DOCUMENT_TYPE_NODE */:
description = “<!DOCTYPE ” + description + “>”;
break;
}
return description;
}

var className = InjectedScriptHost.internalConstructorName(obj);
if (subtype === “array”) {
if (typeof obj.length === “number”)
className += “[” + obj.length + “]”;
return className;
}

// NodeList in JSC is a function, check for array prior to this.
if (typeof obj === “function”)
return toString(obj);

if (isSymbol(obj)) {
try {
return /** @type {string} */ (InjectedScriptHost.callFunction(Symbol.prototype.toString, obj)) || “Symbol”;
} catch (e) {
return “Symbol”;
}
}

if (InjectedScriptHost.subtype(obj) === “error”) {
try {
var stack = obj.stack;
var message = obj.message && obj.message.length ? “: ” + obj.message : “”;
var stackMessageEnd = stack ? indexOf(stack, “\n”) : -1;
if (stackMessageEnd !== -1) {
var stackTrace = stack.substr(stackMessageEnd + 1);
return className + message + “\n” + stackTrace;
}
return className + message;
} catch(e) {
}
}

return className;
},

/**
* @param {boolean} enabled
*/
setCustomObjectFormatterEnabled: function(enabled)
{
this._customObjectFormatterEnabled = enabled;
}
}

/**
* @type {!InjectedScript}
* @const
*/
var injectedScript = new InjectedScript();

/**
* @constructor
* @param {*} object
* @param {string=} objectGroupName
* @param {boolean=} doNotBind
* @param {boolean=} forceValueType
* @param {boolean=} generatePreview
* @param {?Array.<string>=} columnNames
* @param {boolean=} isTable
* @param {boolean=} skipEntriesPreview
* @param {*=} customObjectConfig
*/
InjectedScript.RemoteObject = function(object, objectGroupName, doNotBind, forceValueType, generatePreview, columnNames, isTable, skipEntriesPreview, customObjectConfig)
{
this.type = typeof object;
if (this.type === “undefined” && injectedScript._isHTMLAllCollection(object))
this.type = “object”;

if (injectedScript.isPrimitiveValue(object) || object === null || forceValueType) {
// We don’t send undefined values over JSON.
if (this.type !== “undefined”)
this.value = object;

// Null object is object with ‘null’ subtype.
if (object === null)
this.subtype = “null”;

// Provide user-friendly number values.
if (this.type === “number”) {
this.description = toStringDescription(object);
// Override “value” property for values that can not be JSON-stringified.
switch (this.description) {
case “NaN”:
case “Infinity”:
case “-Infinity”:
case “-0”:
this.value = this.description;
break;
}
}

return;
}

object = /** @type {!Object} */ (object);

if (!doNotBind)
this.objectId = injectedScript._bind(object, objectGroupName);
var subtype = injectedScript._subtype(object);
if (subtype)
this.subtype = subtype;
var className = InjectedScriptHost.internalConstructorName(object);
if (className)
this.className = className;
this.description = injectedScript._describe(object);

if (generatePreview && this.type === “object” && this.subtype !== “node”)
this.preview = this._generatePreview(object, undefined, columnNames, isTable, skipEntriesPreview);

if (injectedScript._customObjectFormatterEnabled) {
var customPreview = this._customPreview(object, objectGroupName, customObjectConfig);
if (customPreview)
this.customPreview = customPreview;
}
}

InjectedScript.RemoteObject.prototype = {

/**
* @param {*} object
* @param {string=} objectGroupName
* @param {*=} customObjectConfig
* @return {?RuntimeAgent.CustomPreview}
*/
_customPreview: function(object, objectGroupName, customObjectConfig)
{
/**
* @param {!Error} error
*/
function logError(error)
{
Promise.resolve().then(inspectedWindow.console.error.bind(inspectedWindow.console, “Custom Formatter Failed: ” + error.message));
}

try {
var formatters = inspectedWindow[“devtoolsFormatters”];
if (!formatters || !isArrayLike(formatters))
return null;

for (var i = 0; i < formatters.length; ++i) {
try {
var formatted = formatters[i].header(object, customObjectConfig);
if (!formatted)
continue;

var hasBody = formatters[i].hasBody(object, customObjectConfig);
injectedScript._substituteObjectTagsInCustomPreview(objectGroupName, formatted);
var formatterObjectId = injectedScript._bind(formatters[i], objectGroupName);
var result = {header: JSON.stringify(formatted), hasBody: !!hasBody, formatterObjectId: formatterObjectId};
if (customObjectConfig)
result[“configObjectId”] = injectedScript._bind(customObjectConfig, objectGroupName);
return result;
} catch (e) {
logError(e);
}
}
} catch (e) {
logError(e);
}
return null;
},

/**
* @return {!RuntimeAgent.ObjectPreview} preview
*/
_createEmptyPreview: function()
{
var preview = {
type: /** @type {!RuntimeAgent.ObjectPreviewType.<string>} */ (this.type),
description: this.description || toStringDescription(this.value),
lossless: true,
overflow: false,
properties: [],
__proto__: null
};
if (this.subtype)
preview.subtype = /** @type {!RuntimeAgent.ObjectPreviewSubtype.<string>} */ (this.subtype);
return preview;
},

/**
* @param {!Object} object
* @param {?Array.<string>=} firstLevelKeys
* @param {?Array.<string>=} secondLevelKeys
* @param {boolean=} isTable
* @param {boolean=} skipEntriesPreview
* @return {!RuntimeAgent.ObjectPreview} preview
*/
_generatePreview: function(object, firstLevelKeys, secondLevelKeys, isTable, skipEntriesPreview)
{
var preview = this._createEmptyPreview();
var firstLevelKeysCount = firstLevelKeys ? firstLevelKeys.length : 0;

var propertiesThreshold = {
properties: isTable ? 1000 : max(5, firstLevelKeysCount),
indexes: isTable ? 1000 : max(100, firstLevelKeysCount),
__proto__: null
};

try {
var descriptors = injectedScript._propertyDescriptors(object, undefined, undefined, firstLevelKeys);

this._appendPropertyDescriptors(preview, descriptors, propertiesThreshold, secondLevelKeys, isTable);
if (propertiesThreshold.indexes < 0 || propertiesThreshold.properties < 0)
return preview;

// Add internal properties to preview.
var internalProperties = InjectedScriptHost.getInternalProperties(object) || [];
for (var i = 0; i < internalProperties.length; ++i) {
internalProperties[i] = nullifyObjectProto(internalProperties[i]);
internalProperties[i].isOwn = true;
internalProperties[i].enumerable = true;
}
this._appendPropertyDescriptors(preview, internalProperties, propertiesThreshold, secondLevelKeys, isTable);

if (this.subtype === “map” || this.subtype === “set” || this.subtype === “iterator”)
this._appendEntriesPreview(object, preview, skipEntriesPreview);

} catch (e) {
preview.lossless = false;
}

return preview;
},

/**
* @param {!RuntimeAgent.ObjectPreview} preview
* @param {!Array.<*>|!Iterable.<*>} descriptors
* @param {!Object} propertiesThreshold
* @param {?Array.<string>=} secondLevelKeys
* @param {boolean=} isTable
*/
_appendPropertyDescriptors: function(preview, descriptors, propertiesThreshold, secondLevelKeys, isTable)
{
for (var descriptor of descriptors) {
if (propertiesThreshold.indexes < 0 || propertiesThreshold.properties < 0)
break;
if (!descriptor)
continue;
if (descriptor.wasThrown) {
preview.lossless = false;
continue;
}

var name = descriptor.name;

// Ignore __proto__ property, stay lossless.
if (name === “__proto__”)
continue;

// Ignore non-enumerable members on prototype, stay lossless.
if (!descriptor.isOwn && !descriptor.enumerable)
continue;

// Ignore length property of array, stay lossless.
if (this.subtype === “array” && name === “length”)
continue;

// Ignore size property of map, set, stay lossless.
if ((this.subtype === “map” || this.subtype === “set”) && name === “size”)
continue;

// Never preview prototype properties, turn lossy.
if (!descriptor.isOwn) {
preview.lossless = false;
continue;
}

// Ignore computed properties, turn lossy.
if (!(“value” in descriptor)) {
preview.lossless = false;
continue;
}

var value = descriptor.value;
var type = typeof value;

// Never render functions in object preview, turn lossy
if (type === “function” && (this.subtype !== “array” || !isUInt32(name))) {
preview.lossless = false;
continue;
}

// Special-case HTMLAll.
if (type === “undefined” && injectedScript._isHTMLAllCollection(value))
type = “object”;

// Render own properties.
if (value === null) {
this._appendPropertyPreview(preview, { name: name, type: “object”, subtype: “null”, value: “null”, __proto__: null }, propertiesThreshold);
continue;
}

var maxLength = 100;
if (InjectedScript.primitiveTypes[type]) {
if (type === “string” && value.length > maxLength) {
value = this._abbreviateString(value, maxLength, true);
preview.lossless = false;
}
this._appendPropertyPreview(preview, { name: name, type: type, value: toStringDescription(value), __proto__: null }, propertiesThreshold);
continue;
}

var property = { name: name, type: type, __proto__: null };
var subtype = injectedScript._subtype(value);
if (subtype)
property.subtype = subtype;

if (secondLevelKeys === null || secondLevelKeys) {
var subPreview = this._generatePreview(value, secondLevelKeys || undefined, undefined, isTable);
property.valuePreview = subPreview;
if (!subPreview.lossless)
preview.lossless = false;
if (subPreview.overflow)
preview.overflow = true;
} else {
var description = “”;
if (type !== “function”)
description = this._abbreviateString(/** @type {string} */ (injectedScript._describe(value)), maxLength, subtype === “regexp”);
property.value = description;
preview.lossless = false;
}
this._appendPropertyPreview(preview, property, propertiesThreshold);
}
},

/**
* @param {!RuntimeAgent.ObjectPreview} preview
* @param {!Object} property
* @param {!Object} propertiesThreshold
*/
_appendPropertyPreview: function(preview, property, propertiesThreshold)
{
if (toString(property.name >>> 0) === property.name)
propertiesThreshold.indexes–;
else
propertiesThreshold.properties–;
if (propertiesThreshold.indexes < 0 || propertiesThreshold.properties < 0) {
preview.overflow = true;
preview.lossless = false;
} else {
push(preview.properties, property);
}
},

/**
* @param {!Object} object
* @param {!RuntimeAgent.ObjectPreview} preview
* @param {boolean=} skipEntriesPreview
*/
_appendEntriesPreview: function(object, preview, skipEntriesPreview)
{
var entries = InjectedScriptHost.collectionEntries(object);
if (!entries)
return;
if (skipEntriesPreview) {
if (entries.length) {
preview.overflow = true;
preview.lossless = false;
}
return;
}
preview.entries = [];
var entriesThreshold = 5;
for (var i = 0; i < entries.length; ++i) {
if (preview.entries.length >= entriesThreshold) {
preview.overflow = true;
preview.lossless = false;
break;
}
var entry = nullifyObjectProto(entries[i]);
var previewEntry = {
value: generateValuePreview(entry.value),
__proto__: null
};
if (“key” in entry)
previewEntry.key = generateValuePreview(entry.key);
push(preview.entries, previewEntry);
}

/**
* @param {*} value
* @return {!RuntimeAgent.ObjectPreview}
*/
function generateValuePreview(value)
{
var remoteObject = new InjectedScript.RemoteObject(value, undefined, true, undefined, true, undefined, undefined, true);
var valuePreview = remoteObject.preview || remoteObject._createEmptyPreview();
if (!valuePreview.lossless)
preview.lossless = false;
return valuePreview;
}
},

/**
* @param {string} string
* @param {number} maxLength
* @param {boolean=} middle
* @return {string}
*/
_abbreviateString: function(string, maxLength, middle)
{
if (string.length <= maxLength)
return string;
if (middle) {
var leftHalf = maxLength >> 1;
var rightHalf = maxLength – leftHalf – 1;
return string.substr(0, leftHalf) + “\u2026” + string.substr(string.length – rightHalf, rightHalf);
}
return string.substr(0, maxLength) + “\u2026”;
},

__proto__: null
}

/**
* @constructor
* @param {number} ordinal
* @param {!JavaScriptCallFrame} callFrame
* @param {number} asyncOrdinal
*/
InjectedScript.CallFrameProxy = function(ordinal, callFrame, asyncOrdinal)
{
this.callFrameId = “{\”ordinal\”:” + ordinal + “,\”injectedScriptId\”:” + injectedScriptId + (asyncOrdinal ? “,\”asyncOrdinal\”:” + asyncOrdinal : “”) + “}”;
this.functionName = callFrame.functionName;
this.functionLocation = { scriptId: toString(callFrame.sourceID), lineNumber: callFrame.functionLine, columnNumber: callFrame.functionColumn, __proto__: null };
this.location = { scriptId: toString(callFrame.sourceID), lineNumber: callFrame.line, columnNumber: callFrame.column, __proto__: null };
this.scopeChain = this._wrapScopeChain(callFrame);
this.this = injectedScript._wrapObject(callFrame.thisObject, “backtrace”);
if (callFrame.isAtReturn)
this.returnValue = injectedScript._wrapObject(callFrame.returnValue, “backtrace”);
}

InjectedScript.CallFrameProxy.prototype = {
/**
* @param {!JavaScriptCallFrame} callFrame
* @return {!Array.<!DebuggerAgent.Scope>}
*/
_wrapScopeChain: function(callFrame)
{
var scopeChain = callFrame.scopeChain;
var scopeChainProxy = [];
for (var i = 0; i < scopeChain.length; ++i)
scopeChainProxy[i] = InjectedScript.CallFrameProxy._createScopeJson(callFrame.scopeType(i), scopeChain[i], “backtrace”);
return scopeChainProxy;
},

__proto__: null
}

/**
* @const
* @type {!Object.<number, !DebuggerAgent.ScopeType>}
*/
InjectedScript.CallFrameProxy._scopeTypeNames = {
0: “global”,
1: “local”,
2: “with”,
3: “closure”,
4: “catch”,
5: “block”,
6: “script”,
__proto__: null
};

/**
* @param {number} scopeTypeCode
* @param {*} scopeObject
* @param {string} groupId
* @return {!DebuggerAgent.Scope}
*/
InjectedScript.CallFrameProxy._createScopeJson = function(scopeTypeCode, scopeObject, groupId)
{
return {
object: injectedScript._wrapObject(scopeObject, groupId),
type: InjectedScript.CallFrameProxy._scopeTypeNames[scopeTypeCode],
__proto__: null
};
}

/**
* @constructor
* @param {!CommandLineAPIImpl} commandLineAPIImpl
* @param {?JavaScriptCallFrame} callFrame
*/
function CommandLineAPI(commandLineAPIImpl, callFrame)
{
/**
* @param {string} member
* @return {boolean}
*/
function inScopeVariables(member)
{
if (!callFrame)
return (member in inspectedWindow);

var scopeChain = callFrame.scopeChain;
for (var i = 0; i < scopeChain.length; ++i) {
if (member in scopeChain[i])
return true;
}
return false;
}

/**
* @param {string} name The name of the method for which a toString method should be generated.
* @return {function():string}
*/
function customToStringMethod(name)
{
return function()
{
var funcArgsSyntax = “”;
try {
var funcSyntax = “” + commandLineAPIImpl[name];
funcSyntax = funcSyntax.replace(/\n/g, ” “);
funcSyntax = funcSyntax.replace(/^function[^\(]*\(([^\)]*)\).*$/, “$1”);
funcSyntax = funcSyntax.replace(/\s*,\s*/g, “, “);
funcSyntax = funcSyntax.replace(/\bopt_(\w+)\b/g, “[$1]”);
funcArgsSyntax = funcSyntax.trim();
} catch (e) {
}
return “function ” + name + “(” + funcArgsSyntax + “) { [Command Line API] }”;
};
}

for (var i = 0; i < CommandLineAPI.members_.length; ++i) {
var member = CommandLineAPI.members_[i];
if (inScopeVariables(member))
continue;

this[member] = bind(commandLineAPIImpl[member], commandLineAPIImpl);
this[member].toString = customToStringMethod(member);
}

for (var i = 0; i < 5; ++i) {
var member = “$” + i;
if (inScopeVariables(member))
continue;

this.__defineGetter__(“$” + i, bind(commandLineAPIImpl._inspectedObject, commandLineAPIImpl, i));
}

this.$_ = injectedScript._lastResult;

this.__proto__ = null;
}

// NOTE: Please keep the list of API methods below snchronized to that in WebInspector.RuntimeModel!
// NOTE: Argument names of these methods will be printed in the console, so use pretty names!
/**
* @type {!Array.<string>}
* @const
*/
CommandLineAPI.members_ = [
“$”, “$$”, “$x”, “dir”, “dirxml”, “keys”, “values”, “profile”, “profileEnd”,
“monitorEvents”, “unmonitorEvents”, “inspect”, “copy”, “clear”, “getEventListeners”,
“debug”, “undebug”, “monitor”, “unmonitor”, “table”
];

/**
* @constructor
*/
function CommandLineAPIImpl()
{
}

CommandLineAPIImpl.prototype = {
/**
* @param {string} selector
* @param {!Node=} opt_startNode
* @return {*}
*/
$: function (selector, opt_startNode)
{
if (this._canQuerySelectorOnNode(opt_startNode))
return opt_startNode.querySelector(selector);

return inspectedWindow.document.querySelector(selector);
},

/**
* @param {string} selector
* @param {!Node=} opt_startNode
* @return {*}
*/
$$: function (selector, opt_startNode)
{
if (this._canQuerySelectorOnNode(opt_startNode))
return opt_startNode.querySelectorAll(selector);
return inspectedWindow.document.querySelectorAll(selector);
},

/**
* @param {!Node=} node
* @return {boolean}
*/
_canQuerySelectorOnNode: function(node)
{
return !!node && InjectedScriptHost.subtype(node) === “node” && (node.nodeType === Node.ELEMENT_NODE || node.nodeType === Node.DOCUMENT_NODE || node.nodeType === Node.DOCUMENT_FRAGMENT_NODE);
},

/**
* @param {string} xpath
* @param {!Node=} opt_startNode
* @return {*}
*/
$x: function(xpath, opt_startNode)
{
var doc = (opt_startNode && opt_startNode.ownerDocument) || inspectedWindow.document;
var result = doc.evaluate(xpath, opt_startNode || doc, null, XPathResult.ANY_TYPE, null);
switch (result.resultType) {
case XPathResult.NUMBER_TYPE:
return result.numberValue;
case XPathResult.STRING_TYPE:
return result.stringValue;
case XPathResult.BOOLEAN_TYPE:
return result.booleanValue;
default:
var nodes = [];
var node;
while (node = result.iterateNext())
push(nodes, node);
return nodes;
}
},

/**
* @return {*}
*/
dir: function(var_args)
{
return InjectedScriptHost.callFunction(inspectedWindow.console.dir, inspectedWindow.console, slice(arguments));
},

/**
* @return {*}
*/
dirxml: function(var_args)
{
return InjectedScriptHost.callFunction(inspectedWindow.console.dirxml, inspectedWindow.console, slice(arguments));
},

/**
* @return {!Array.<string>}
*/
keys: function(object)
{
return Object.keys(object);
},

/**
* @return {!Array.<*>}
*/
values: function(object)
{
var result = [];
for (var key in object)
push(result, object[key]);
return result;
},

/**
* @return {*}
*/
profile: function(opt_title)
{
return InjectedScriptHost.callFunction(inspectedWindow.console.profile, inspectedWindow.console, slice(arguments));
},

/**
* @return {*}
*/
profileEnd: function(opt_title)
{
return InjectedScriptHost.callFunction(inspectedWindow.console.profileEnd, inspectedWindow.console, slice(arguments));
},

/**
* @param {!Object} object
* @param {!Array.<string>|string=} opt_types
*/
monitorEvents: function(object, opt_types)
{
if (!object || !object.addEventListener || !object.removeEventListener)
return;
var types = this._normalizeEventTypes(opt_types);
for (var i = 0; i < types.length; ++i) {
object.removeEventListener(types[i], this._logEvent, false);
object.addEventListener(types[i], this._logEvent, false);
}
},

/**
* @param {!Object} object
* @param {!Array.<string>|string=} opt_types
*/
unmonitorEvents: function(object, opt_types)
{
if (!object || !object.addEventListener || !object.removeEventListener)
return;
var types = this._normalizeEventTypes(opt_types);
for (var i = 0; i < types.length; ++i)
object.removeEventListener(types[i], this._logEvent, false);
},

/**
* @param {*} object
* @return {*}
*/
inspect: function(object)
{
return injectedScript._inspect(object);
},

copy: function(object)
{
var string;
if (injectedScript._subtype(object) === “node”) {
string = object.outerHTML;
} else if (injectedScript.isPrimitiveValue(object)) {
string = toString(object);
} else {
try {
string = JSON.stringify(object, null, ” “);
} catch (e) {
string = toString(object);
}
}

var hints = { copyToClipboard: true, __proto__: null };
var remoteObject = injectedScript._wrapObject(string, “”)
InjectedScriptHost.inspect(remoteObject, hints);
},

clear: function()
{
InjectedScriptHost.clearConsoleMessages();
},

/**
* @param {!Node} node
* @return {!Array.<!{type: string, listener: function(), useCapture: boolean, remove: function()}>|undefined}
*/
getEventListeners: function(node)
{
var result = nullifyObjectProto(InjectedScriptHost.getEventListeners(node));
if (!result)
return result;
/** @this {{type: string, listener: function(), useCapture: boolean}} */
var removeFunc = function()
{
node.removeEventListener(this.type, this.listener, this.useCapture);
}
for (var type in result) {
var listeners = result[type];
for (var i = 0, listener; listener = listeners[i]; ++i) {
listener[“type”] = type;
listener[“remove”] = removeFunc;
}
}
return result;
},

debug: function(fn)
{
InjectedScriptHost.debugFunction(fn);
},

undebug: function(fn)
{
InjectedScriptHost.undebugFunction(fn);
},

monitor: function(fn)
{
InjectedScriptHost.monitorFunction(fn);
},

unmonitor: function(fn)
{
InjectedScriptHost.unmonitorFunction(fn);
},

table: function(data, opt_columns)
{
InjectedScriptHost.callFunction(inspectedWindow.console.table, inspectedWindow.console, slice(arguments));
},

/**
* @param {number} num
*/
_inspectedObject: function(num)
{
return InjectedScriptHost.inspectedObject(num);
},

/**
* @param {!Array.<string>|string=} types
* @return {!Array.<string>}
*/
_normalizeEventTypes: function(types)
{
if (typeof types === “undefined”)
types = [“mouse”, “key”, “touch”, “control”, “load”, “unload”, “abort”, “error”, “select”, “input”, “change”, “submit”, “reset”, “focus”, “blur”, “resize”, “scroll”, “search”, “devicemotion”, “deviceorientation”];
else if (typeof types === “string”)
types = [types];

var result = [];
for (var i = 0; i < types.length; ++i) {
if (types[i] === “mouse”)
push(result, “mousedown”, “mouseup”, “click”, “dblclick”, “mousemove”, “mouseover”, “mouseout”, “mousewheel”);
else if (types[i] === “key”)
push(result, “keydown”, “keyup”, “keypress”, “textInput”);
else if (types[i] === “touch”)
push(result, “touchstart”, “touchmove”, “touchend”, “touchcancel”);
else if (types[i] === “control”)
push(result, “resize”, “scroll”, “zoom”, “focus”, “blur”, “select”, “input”, “change”, “submit”, “reset”);
else
push(result, types[i]);
}
return result;
},

/**
* @param {!Event} event
*/
_logEvent: function(event)
{
inspectedWindow.console.log(event.type, event);
}
}

injectedScript._commandLineAPIImpl = new CommandLineAPIImpl();
return injectedScript;
})

Categories: Uncategorized

IP info popup in Javascript

Here’s a code snippet using Bootstrap / Jquery / bootbox / telize to provide info on an IP address via Javacript / JSONP

<html>
<head>
https://code.jquery.com/jquery-1.11.3.min.js
https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js
https://cdnjs.cloudflare.com/ajax/libs/bootbox.js/4.4.0/bootbox.min.js
<link href=”https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css&#8221; rel=”stylesheet”>
</head>
<body>
<span class=”ip”>64.233.167.106</span>
</body>

$(init);
function init()
{
$(“.ip”).bind(“click”,ip_click);
}
function ip_click()
{
var ip = $(this).text();
var strUrl = “http://www.telize.com/geoip/&#8221; + ip + “?callback=getgeoip”;
$.ajax({
url: strUrl,
jsonp: “getgeoip”,
dataType: “jsonp”,
});
}
function getgeoip(data)
{
var strHtml = “IP: ” + data.ip + “
“;
strHtml += “” + data.city + “, ” + data.region + “, ” + data.country + “
“;
strHtml += “ISP: ” + data.isp + ““;
bootbox.alert(strHtml);
}

</html>

Categories: Uncategorized