add blank page

add chrome gpu stuff (doesn't work)
dump out browser cookies
This commit is contained in:
Jeff Carr 2017-11-30 23:08:41 +00:00
parent 72a5a63bb5
commit de2ad5a02f
16 changed files with 3276 additions and 3 deletions

9
blank.html Normal file
View File

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<title>check.wit.com</title>
</head>
<body>
</body>
</html>

29
chrome/action_link.css Normal file
View File

@ -0,0 +1,29 @@
/* Copyright 2015 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. */
[is='action-link'] {
cursor: pointer;
display: inline-block;
text-decoration: none;
}
[is='action-link']:hover {
text-decoration: underline;
}
[is='action-link']:active {
color: rgb(5, 37, 119);
text-decoration: underline;
}
[is='action-link'][disabled] {
color: #999;
cursor: default;
pointer-events: none;
text-decoration: none;
}
[is='action-link'].no-outline {
outline: none;
}

View File

@ -0,0 +1,346 @@
// 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.
var commandLineFlags = ['--flag-switches-begin',
'--show-composited-layer-borders',
'--show-fps-counter',
'--flag-switches-end'];
var commandLineStr = './out/Debug/chrome ' + commandLineFlags.join(' ');
var glValueArray = ['GL_ARB_compatibility',
'GL_ARB_copy_buffer',
'GL_ARB_depth_buffer_float',
'GL_ARB_depth_clamp',
'GL_ARB_depth_texture',
'GL_ARB_draw_buffers',
'GL_ARB_draw_elements_base_vertex',
'GL_ARB_draw_instanced',
'GL_ARB_fragment_coord_conventions',
'GL_ARB_fragment_program',
'GL_ARB_fragment_program_shadow',
'GL_ARB_fragment_shader',
'GL_ARB_framebuffer_object',
'GL_ARB_framebuffer_sRGB',
'GL_ARB_geometry_shader4',
'GL_ARB_half_float_pixel',
'GL_ARB_half_float_vertex',
'GL_ARB_imaging',
'GL_ARB_map_buffer_range',
'GL_ARB_multisample',
'GL_ARB_multitexture',
'GL_ARB_occlusion_query',
'GL_ARB_pixel_buffer_object',
'GL_ARB_point_parameters',
'GL_ARB_point_sprite',
'GL_ARB_provoking_vertex',
'GL_ARB_seamless_cube_map',
'GL_ARB_shader_objects',
'GL_ARB_shading_language_100',
'GL_ARB_shadow',
'GL_ARB_sync',
'GL_ARB_texture_border_clamp',
'GL_ARB_texture_buffer_object',
'GL_ARB_texture_compression',
'GL_ARB_texture_compression_rgtc',
'GL_ARB_texture_cube_map',
'GL_ARB_texture_env_add',
'GL_ARB_texture_env_combine',
'GL_ARB_texture_env_crossbar',
'GL_ARB_texture_env_dot3',
'GL_ARB_texture_float',
'GL_ARB_texture_mirrored_repeat',
'GL_ARB_texture_multisample',
'GL_ARB_texture_non_power_of_two',
'GL_ARB_texture_rectangle',
'GL_ARB_texture_rg',
'GL_ARB_transpose_matrix',
'GL_ARB_uniform_buffer_object',
'GL_ARB_vertex_array_bgra',
'GL_ARB_vertex_array_object',
'GL_ARB_vertex_buffer_object',
'GL_ARB_vertex_program',
'GL_ARB_vertex_shader',
'GL_ARB_window_pos',
'GL_ATI_draw_buffers',
'GL_ATI_texture_float',
'GL_ATI_texture_mirror_once',
'GL_S3_s3tc',
'GL_EXT_texture_env_add',
'GL_EXT_abgr',
'GL_EXT_bgra',
'GL_EXT_bindable_uniform',
'GL_EXT_blend_color',
'GL_EXT_blend_equation_separate',
'GL_EXT_blend_func_separate',
'GL_EXT_blend_minmax',
'GL_EXT_blend_subtract',
'GL_EXT_compiled_vertex_array',
'GL_EXT_Cg_shader',
'GL_EXT_depth_bounds_test',
'GL_EXT_direct_state_access',
'GL_EXT_draw_buffers2',
'GL_EXT_draw_instanced',
'GL_EXT_draw_range_elements',
'GL_EXT_fog_coord',
'GL_EXT_framebuffer_blit',
'GL_EXT_framebuffer_multisample',
'GL_EXTX_framebuffer_mixed_formats',
'GL_EXT_framebuffer_object',
'GL_EXT_framebuffer_sRGB',
'GL_EXT_geometry_shader4',
'GL_EXT_gpu_program_parameters',
'GL_EXT_gpu_shader4',
'GL_EXT_multi_draw_arrays',
'GL_EXT_packed_depth_stencil',
'GL_EXT_packed_float',
'GL_EXT_packed_pixels',
'GL_EXT_pixel_buffer_object',
'GL_EXT_point_parameters',
'GL_EXT_provoking_vertex',
'GL_EXT_rescale_normal',
'GL_EXT_secondary_color',
'GL_EXT_separate_shader_objects',
'GL_EXT_separate_specular_color',
'GL_EXT_shadow_funcs',
'GL_EXT_stencil_two_side',
'GL_EXT_stencil_wrap',
'GL_EXT_texture3D',
'GL_EXT_texture_array',
'GL_EXT_texture_buffer_object',
'GL_EXT_texture_compression_latc',
'GL_EXT_texture_compression_rgtc',
'GL_EXT_texture_compression_s3tc',
'GL_EXT_texture_cube_map',
'GL_EXT_texture_edge_clamp',
'GL_EXT_texture_env_combine',
'GL_EXT_texture_env_dot3',
'GL_EXT_texture_filter_anisotropic',
'GL_EXT_texture_integer',
'GL_EXT_texture_lod',
'GL_EXT_texture_lod_bias',
'GL_EXT_texture_mirror_clamp',
'GL_EXT_texture_object',
'GL_EXT_texture_shared_exponent',
'GL_EXT_texture_sRGB',
'GL_EXT_texture_swizzle',
'GL_EXT_timer_query',
'GL_EXT_vertex_array',
'GL_EXT_vertex_array_bgra',
'GL_IBM_rasterpos_clip',
'GL_IBM_texture_mirrored_repeat',
'GL_KTX_buffer_region',
'GL_NV_blend_square',
'GL_NV_conditional_render',
'GL_NV_copy_depth_to_color',
'GL_NV_copy_image',
'GL_NV_depth_buffer_float',
'GL_NV_depth_clamp',
'GL_NV_explicit_multisample',
'GL_NV_fence',
'GL_NV_float_buffer',
'GL_NV_fog_distance',
'GL_NV_fragment_program',
'GL_NV_fragment_program_option',
'GL_NV_fragment_program2',
'GL_NV_framebuffer_multisample_coverage',
'GL_NV_geometry_shader4',
'GL_NV_gpu_program4',
'GL_NV_half_float',
'GL_NV_light_max_exponent',
'GL_NV_multisample_coverage',
'GL_NV_multisample_filter_hint',
'GL_NV_occlusion_query',
'GL_NV_packed_depth_stencil',
'GL_NV_parameter_buffer_object',
'GL_NV_parameter_buffer_object2',
'GL_NV_pixel_data_range',
'GL_NV_point_sprite',
'GL_NV_primitive_restart',
'GL_NV_register_combiners',
'GL_NV_register_combiners2',
'GL_NV_shader_buffer_load',
'GL_NV_texgen_reflection',
'GL_NV_texture_barrier',
'GL_NV_texture_compression_vtc',
'GL_NV_texture_env_combine4',
'GL_NV_texture_expand_normal',
'GL_NV_texture_rectangle',
'GL_NV_texture_shader',
'GL_NV_texture_shader2',
'GL_NV_texture_shader3',
'GL_NV_transform_feedback',
'GL_NV_vertex_array_range',
'GL_NV_vertex_array_range2',
'GL_NV_vertex_buffer_unified_memory',
'GL_NV_vertex_program',
'GL_NV_vertex_program1_1',
'GL_NV_vertex_program2',
'GL_NV_vertex_program2_option',
'GL_NV_vertex_program3',
'GL_NVX_conditional_render',
'GL_NVX_gpu_memory_info',
'GL_SGIS_generate_mipmap',
'GL_SGIS_texture_lod',
'GL_SGIX_depth_texture',
'GL_SGIX_shadow',
'GL_SUN_slice_accum'];
(function() {
var dataSets = [
{
name: 'full_data_linux',
gpuInfo: {
basic_info: [
{
description: 'Initialization time',
value: '111'
},
{
description: 'Vendor Id',
value: '0x10de'
},
{
description: 'Device Id',
value: '0x0658'
},
{
description: 'Driver vendor',
value: 'NVIDIA'
},
{
description: 'Driver version',
value: '195.36.24'
},
{
description: 'Driver date',
value: ''
},
{
description: 'Pixel shader version',
value: '1.50'
},
{
description: 'Vertex shader version',
value: '1.50'
},
{
description: 'GL version',
value: '3.2'
},
{
description: 'GL_VENDOR',
value: 'NVIDIA Corporation'
},
{
description: 'GL_RENDERER',
value: 'Quadro FX 380/PCI/SSE2'
},
{
description: 'GL_VERSION',
value: '3.2.0 NVIDIA 195.36.24'
},
{
description: 'GL_EXTENSIONS',
value: glValueArray.join(' '),
}
],
featureStatus: {
featureStatus:
[
{'status': 'enabled', name: '2d_canvas'},
{'status': 'enabled', name: '3d_css'},
{'status': 'enabled', name: 'compositing'},
{'status': 'enabled', name: 'webgl'},
{'status': 'enabled', name: 'multisampling'}
],
problems: []
}
},
clientInfo: {
blacklist_version: '1.10',
command_line: commandLineStr,
version: 'Chrome/12.0.729.0',
},
logMessages: []
},
{
name: 'no_data',
gpuInfo: undefined,
clientInfo: undefined,
logMessages: undefined
},
{
name: 'logs',
gpuInfo: undefined,
clientInfo: undefined,
logMessages: [
{header: 'foo', message: 'Bar'}
]
},
// tests for 'status'
{
name: 'feature_states',
gpuInfo: {
basic_info: undefined,
featureStatus: {
featureStatus: [
{'status': 'disabled_off', name: '2d_canvas'},
{'status': 'unavailable_software', name: '3d_css'},
{'status': 'disabled_software', name: 'compositing'},
{'status': 'software', name: 'compositing'},
{'status': 'unavailable_off', name: 'webgl'},
{'status': 'enabled', name: 'multisampling'}
],
problems: [
{
description: 'Something wrong',
crBugs: [],
webkitBugs: []
},
{
description: 'SomethingElse',
crBugs: [],
webkitBugs: []
},
{
description: 'WebKit and Chrome bug',
crBugs: [23456],
webkitBugs: [789, 2123]
}
]
}
},
clientInfo: undefined,
logMessages: []
}
];
var selectEl = document.createElement('select');
for (var i = 0; i < dataSets.length; ++i) {
var optionEl = document.createElement('option');
optionEl.textContent = dataSets[i].name;
optionEl.dataSet = dataSets[i];
selectEl.add(optionEl);
}
selectEl.addEventListener('change', function() {
browserBridge.applySimulatedData_(dataSets[selectEl.selectedIndex]);
});
selectEl.addEventListener('keydown', function() {
window.setTimeout(function() {
browserBridge.applySimulatedData_(dataSets[selectEl.selectedIndex]);
}, 0);
});
var controlEl = document.createElement('div');
var textEl = document.createElement('span');
textEl.textContent = 'GPU Info:';
controlEl.appendChild(textEl);
controlEl.appendChild(selectEl);
// document.querySelector('#debug-div').appendChild(controlEl, document.body.firstChild);
console.log("hello from browser stuff.js");
browserBridge.applySimulatedData_(dataSets[0]);
})();

489
chrome/cr.js Normal file
View File

@ -0,0 +1,489 @@
// 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.
/**
* The global object.
* @type {!Object}
* @const
*/
var global = this;
/** @typedef {{eventName: string, uid: number}} */
var WebUIListener;
/** Platform, package, object property, and Event support. **/
var cr = cr || function() {
'use strict';
/**
* Builds an object structure for the provided namespace path,
* ensuring that names that already exist are not overwritten. For
* example:
* "a.b.c" -> a = {};a.b={};a.b.c={};
* @param {string} name Name of the object that this file defines.
* @param {*=} opt_object The object to expose at the end of the path.
* @param {Object=} opt_objectToExportTo The object to add the path to;
* default is {@code global}.
* @return {!Object} The last object exported (i.e. exportPath('cr.ui')
* returns a reference to the ui property of window.cr).
* @private
*/
function exportPath(name, opt_object, opt_objectToExportTo) {
var parts = name.split('.');
var cur = opt_objectToExportTo || global;
for (var part; parts.length && (part = parts.shift());) {
if (!parts.length && opt_object !== undefined) {
// last part and we have an object; use it
cur[part] = opt_object;
} else if (part in cur) {
cur = cur[part];
} else {
cur = cur[part] = {};
}
}
return cur;
}
/**
* Fires a property change event on the target.
* @param {EventTarget} target The target to dispatch the event on.
* @param {string} propertyName The name of the property that changed.
* @param {*} newValue The new value for the property.
* @param {*} oldValue The old value for the property.
*/
function dispatchPropertyChange(target, propertyName, newValue, oldValue) {
var e = new Event(propertyName + 'Change');
e.propertyName = propertyName;
e.newValue = newValue;
e.oldValue = oldValue;
target.dispatchEvent(e);
}
/**
* Converts a camelCase javascript property name to a hyphenated-lower-case
* attribute name.
* @param {string} jsName The javascript camelCase property name.
* @return {string} The equivalent hyphenated-lower-case attribute name.
*/
function getAttributeName(jsName) {
return jsName.replace(/([A-Z])/g, '-$1').toLowerCase();
}
/**
* The kind of property to define in {@code defineProperty}.
* @enum {string}
* @const
*/
var PropertyKind = {
/**
* Plain old JS property where the backing data is stored as a "private"
* field on the object.
* Use for properties of any type. Type will not be checked.
*/
JS: 'js',
/**
* The property backing data is stored as an attribute on an element.
* Use only for properties of type {string}.
*/
ATTR: 'attr',
/**
* The property backing data is stored as an attribute on an element. If the
* element has the attribute then the value is true.
* Use only for properties of type {boolean}.
*/
BOOL_ATTR: 'boolAttr'
};
/**
* Helper function for defineProperty that returns the getter to use for the
* property.
* @param {string} name The name of the property.
* @param {PropertyKind} kind The kind of the property.
* @return {function():*} The getter for the property.
*/
function getGetter(name, kind) {
switch (kind) {
case PropertyKind.JS:
var privateName = name + '_';
return function() {
return this[privateName];
};
case PropertyKind.ATTR:
var attributeName = getAttributeName(name);
return function() {
return this.getAttribute(attributeName);
};
case PropertyKind.BOOL_ATTR:
var attributeName = getAttributeName(name);
return function() {
return this.hasAttribute(attributeName);
};
}
// TODO(dbeam): replace with assertNotReached() in assert.js when I can coax
// the browser/unit tests to preprocess this file through grit.
throw 'not reached';
}
/**
* Helper function for defineProperty that returns the setter of the right
* kind.
* @param {string} name The name of the property we are defining the setter
* for.
* @param {PropertyKind} kind The kind of property we are getting the
* setter for.
* @param {function(*, *):void=} opt_setHook A function to run after the
* property is set, but before the propertyChange event is fired.
* @return {function(*):void} The function to use as a setter.
*/
function getSetter(name, kind, opt_setHook) {
switch (kind) {
case PropertyKind.JS:
var privateName = name + '_';
return function(value) {
var oldValue = this[name];
if (value !== oldValue) {
this[privateName] = value;
if (opt_setHook)
opt_setHook.call(this, value, oldValue);
dispatchPropertyChange(this, name, value, oldValue);
}
};
case PropertyKind.ATTR:
var attributeName = getAttributeName(name);
return function(value) {
var oldValue = this[name];
if (value !== oldValue) {
if (value == undefined)
this.removeAttribute(attributeName);
else
this.setAttribute(attributeName, value);
if (opt_setHook)
opt_setHook.call(this, value, oldValue);
dispatchPropertyChange(this, name, value, oldValue);
}
};
case PropertyKind.BOOL_ATTR:
var attributeName = getAttributeName(name);
return function(value) {
var oldValue = this[name];
if (value !== oldValue) {
if (value)
this.setAttribute(attributeName, name);
else
this.removeAttribute(attributeName);
if (opt_setHook)
opt_setHook.call(this, value, oldValue);
dispatchPropertyChange(this, name, value, oldValue);
}
};
}
// TODO(dbeam): replace with assertNotReached() in assert.js when I can coax
// the browser/unit tests to preprocess this file through grit.
throw 'not reached';
}
/**
* Defines a property on an object. When the setter changes the value a
* property change event with the type {@code name + 'Change'} is fired.
* @param {!Object} obj The object to define the property for.
* @param {string} name The name of the property.
* @param {PropertyKind=} opt_kind What kind of underlying storage to use.
* @param {function(*, *):void=} opt_setHook A function to run after the
* property is set, but before the propertyChange event is fired.
*/
function defineProperty(obj, name, opt_kind, opt_setHook) {
if (typeof obj == 'function')
obj = obj.prototype;
var kind = /** @type {PropertyKind} */ (opt_kind || PropertyKind.JS);
if (!obj.__lookupGetter__(name))
obj.__defineGetter__(name, getGetter(name, kind));
if (!obj.__lookupSetter__(name))
obj.__defineSetter__(name, getSetter(name, kind, opt_setHook));
}
/**
* Counter for use with createUid
*/
var uidCounter = 1;
/**
* @return {number} A new unique ID.
*/
function createUid() {
return uidCounter++;
}
/**
* Returns a unique ID for the item. This mutates the item so it needs to be
* an object
* @param {!Object} item The item to get the unique ID for.
* @return {number} The unique ID for the item.
*/
function getUid(item) {
if (item.hasOwnProperty('uid'))
return item.uid;
return item.uid = createUid();
}
/**
* Dispatches a simple event on an event target.
* @param {!EventTarget} target The event target to dispatch the event on.
* @param {string} type The type of the event.
* @param {boolean=} opt_bubbles Whether the event bubbles or not.
* @param {boolean=} opt_cancelable Whether the default action of the event
* can be prevented. Default is true.
* @return {boolean} If any of the listeners called {@code preventDefault}
* during the dispatch this will return false.
*/
function dispatchSimpleEvent(target, type, opt_bubbles, opt_cancelable) {
var e = new Event(type, {
bubbles: opt_bubbles,
cancelable: opt_cancelable === undefined || opt_cancelable
});
return target.dispatchEvent(e);
}
/**
* Calls |fun| and adds all the fields of the returned object to the object
* named by |name|. For example, cr.define('cr.ui', function() {
* function List() {
* ...
* }
* function ListItem() {
* ...
* }
* return {
* List: List,
* ListItem: ListItem,
* };
* });
* defines the functions cr.ui.List and cr.ui.ListItem.
* @param {string} name The name of the object that we are adding fields to.
* @param {!Function} fun The function that will return an object containing
* the names and values of the new fields.
*/
function define(name, fun) {
var obj = exportPath(name);
var exports = fun();
for (var propertyName in exports) {
// Maybe we should check the prototype chain here? The current usage
// pattern is always using an object literal so we only care about own
// properties.
var propertyDescriptor =
Object.getOwnPropertyDescriptor(exports, propertyName);
if (propertyDescriptor)
Object.defineProperty(obj, propertyName, propertyDescriptor);
}
}
/**
* Adds a {@code getInstance} static method that always return the same
* instance object.
* @param {!Function} ctor The constructor for the class to add the static
* method to.
*/
function addSingletonGetter(ctor) {
ctor.getInstance = function() {
return ctor.instance_ || (ctor.instance_ = new ctor());
};
}
/**
* Forwards public APIs to private implementations.
* @param {Function} ctor Constructor that have private implementations in its
* prototype.
* @param {Array<string>} methods List of public method names that have their
* underscored counterparts in constructor's prototype.
* @param {string=} opt_target Selector for target node.
*/
function makePublic(ctor, methods, opt_target) {
methods.forEach(function(method) {
ctor[method] = function() {
var target = opt_target ?
// Disable document.getElementById restriction since cr.js should
// not depend on util.js.
// eslint-disable-next-line no-restricted-properties
document.getElementById(opt_target) :
ctor.getInstance();
return target[method + '_'].apply(target, arguments);
};
});
}
/**
* The mapping used by the sendWithPromise mechanism to tie the Promise
* returned to callers with the corresponding WebUI response. The mapping is
* from ID to the PromiseResolver helper; the ID is generated by
* sendWithPromise and is unique across all invocations of said method.
* @type {!Object<!PromiseResolver>}
*/
var chromeSendResolverMap = {};
/**
* The named method the WebUI handler calls directly in response to a
* chrome.send call that expects a response. The handler requires no knowledge
* of the specific name of this method, as the name is passed to the handler
* as the first argument in the arguments list of chrome.send. The handler
* must pass the ID, also sent via the chrome.send arguments list, as the
* first argument of the JS invocation; additionally, the handler may
* supply any number of other arguments that will be included in the response.
* @param {string} id The unique ID identifying the Promise this response is
* tied to.
* @param {boolean} isSuccess Whether the request was successful.
* @param {*} response The response as sent from C++.
*/
function webUIResponse(id, isSuccess, response) {
var resolver = chromeSendResolverMap[id];
delete chromeSendResolverMap[id];
if (isSuccess)
resolver.resolve(response);
else
resolver.reject(response);
}
/**
* A variation of chrome.send, suitable for messages that expect a single
* response from C++.
* @param {string} methodName The name of the WebUI handler API.
* @param {...*} var_args Variable number of arguments to be forwarded to the
* C++ call.
* @return {!Promise}
*/
function sendWithPromise(methodName, var_args) {
var args = Array.prototype.slice.call(arguments, 1);
var promiseResolver = new PromiseResolver();
var id = methodName + '_' + createUid();
chromeSendResolverMap[id] = promiseResolver;
chrome.send(methodName, [id].concat(args));
return promiseResolver.promise;
}
/**
* A map of maps associating event names with listeners. The 2nd level map
* associates a listener ID with the callback function, such that individual
* listeners can be removed from an event without affecting other listeners of
* the same event.
* @type {!Object<!Object<!Function>>}
*/
var webUIListenerMap = {};
/**
* The named method the WebUI handler calls directly when an event occurs.
* The WebUI handler must supply the name of the event as the first argument
* of the JS invocation; additionally, the handler may supply any number of
* other arguments that will be forwarded to the listener callbacks.
* @param {string} event The name of the event that has occurred.
* @param {...*} var_args Additional arguments passed from C++.
*/
function webUIListenerCallback(event, var_args) {
var eventListenersMap = webUIListenerMap[event];
if (!eventListenersMap) {
// C++ event sent for an event that has no listeners.
// TODO(dpapad): Should a warning be displayed here?
return;
}
var args = Array.prototype.slice.call(arguments, 1);
for (var listenerId in eventListenersMap) {
eventListenersMap[listenerId].apply(null, args);
}
}
/**
* Registers a listener for an event fired from WebUI handlers. Any number of
* listeners may register for a single event.
* @param {string} eventName The event to listen to.
* @param {!Function} callback The callback run when the event is fired.
* @return {!WebUIListener} An object to be used for removing a listener via
* cr.removeWebUIListener. Should be treated as read-only.
*/
function addWebUIListener(eventName, callback) {
webUIListenerMap[eventName] = webUIListenerMap[eventName] || {};
var uid = createUid();
webUIListenerMap[eventName][uid] = callback;
return {eventName: eventName, uid: uid};
}
/**
* Removes a listener. Does nothing if the specified listener is not found.
* @param {!WebUIListener} listener The listener to be removed (as returned by
* addWebUIListener).
* @return {boolean} Whether the given listener was found and actually
* removed.
*/
function removeWebUIListener(listener) {
var listenerExists = webUIListenerMap[listener.eventName] &&
webUIListenerMap[listener.eventName][listener.uid];
if (listenerExists) {
delete webUIListenerMap[listener.eventName][listener.uid];
return true;
}
return false;
}
return {
addSingletonGetter: addSingletonGetter,
createUid: createUid,
define: define,
defineProperty: defineProperty,
dispatchPropertyChange: dispatchPropertyChange,
dispatchSimpleEvent: dispatchSimpleEvent,
exportPath: exportPath,
getUid: getUid,
makePublic: makePublic,
PropertyKind: PropertyKind,
// C++ <-> JS communication related methods.
addWebUIListener: addWebUIListener,
removeWebUIListener: removeWebUIListener,
sendWithPromise: sendWithPromise,
webUIListenerCallback: webUIListenerCallback,
webUIResponse: webUIResponse,
get doc() {
return document;
},
/** Whether we are using a Mac or not. */
get isMac() {
return /Mac/.test(navigator.platform);
},
/** Whether this is on the Windows platform or not. */
get isWindows() {
return /Win/.test(navigator.platform);
},
/** Whether this is on chromeOS or not. */
get isChromeOS() {
return /CrOS/.test(navigator.userAgent);
},
/** Whether this is on vanilla Linux (not chromeOS). */
get isLinux() {
return /Linux/.test(navigator.userAgent);
},
/** Whether this is on Android. */
get isAndroid() {
return /Android/.test(navigator.userAgent);
},
/** Whether this is on iOS. */
get isIOS() {
return /iPad|iPhone|iPod/.test(navigator.platform);
}
};
}();

102
chrome/event_target.js Normal file
View File

@ -0,0 +1,102 @@
// Copyright (c) 2010 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.
/**
* @fileoverview This contains an implementation of the EventTarget interface
* as defined by DOM Level 2 Events.
*/
/**
* @typedef {EventListener|function(!Event):*}
*/
var EventListenerType;
cr.define('cr', function() {
/**
* Creates a new EventTarget. This class implements the DOM level 2
* EventTarget interface and can be used wherever those are used.
* @constructor
* @implements {EventTarget}
*/
function EventTarget() {}
EventTarget.prototype = {
/**
* Adds an event listener to the target.
* @param {string} type The name of the event.
* @param {EventListenerType} handler The handler for the event. This is
* called when the event is dispatched.
*/
addEventListener: function(type, handler) {
if (!this.listeners_)
this.listeners_ = Object.create(null);
if (!(type in this.listeners_)) {
this.listeners_[type] = [handler];
} else {
var handlers = this.listeners_[type];
if (handlers.indexOf(handler) < 0)
handlers.push(handler);
}
},
/**
* Removes an event listener from the target.
* @param {string} type The name of the event.
* @param {EventListenerType} handler The handler for the event.
*/
removeEventListener: function(type, handler) {
if (!this.listeners_)
return;
if (type in this.listeners_) {
var handlers = this.listeners_[type];
var index = handlers.indexOf(handler);
if (index >= 0) {
// Clean up if this was the last listener.
if (handlers.length == 1)
delete this.listeners_[type];
else
handlers.splice(index, 1);
}
}
},
/**
* Dispatches an event and calls all the listeners that are listening to
* the type of the event.
* @param {!Event} event The event to dispatch.
* @return {boolean} Whether the default action was prevented. If someone
* calls preventDefault on the event object then this returns false.
*/
dispatchEvent: function(event) {
if (!this.listeners_)
return true;
// Since we are using DOM Event objects we need to override some of the
// properties and methods so that we can emulate this correctly.
var self = this;
event.__defineGetter__('target', function() {
return self;
});
var type = event.type;
var prevented = 0;
if (type in this.listeners_) {
// Clone to prevent removal during dispatch
var handlers = this.listeners_[type].concat();
for (var i = 0, handler; handler = handlers[i]; i++) {
if (handler.handleEvent)
prevented |= handler.handleEvent.call(handler, event) === false;
else
prevented |= handler.call(this, event) === false;
}
}
return !prevented && !event.defaultPrevented;
}
};
// Export
return {EventTarget: EventTarget};
});

View File

@ -0,0 +1,106 @@
// 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.
cr.define('cr.ui', function() {
/**
* The class name to set on the document element.
* @const
*/
var CLASS_NAME = 'focus-outline-visible';
/**
* This class sets a CSS class name on the HTML element of |doc| when the user
* presses the tab key. It removes the class name when the user clicks
* anywhere.
*
* This allows you to write CSS like this:
*
* html.focus-outline-visible my-element:focus {
* outline: 5px auto -webkit-focus-ring-color;
* }
*
* And the outline will only be shown if the user uses the keyboard to get to
* it.
*
* @param {Document} doc The document to attach the focus outline manager to.
* @constructor
*/
function FocusOutlineManager(doc) {
this.classList_ = doc.documentElement.classList;
var self = this;
doc.addEventListener('keydown', function(e) {
self.focusByKeyboard_ = true;
}, true);
doc.addEventListener('mousedown', function(e) {
self.focusByKeyboard_ = false;
}, true);
doc.addEventListener('focus', function(event) {
// Update visibility only when focus is actually changed.
self.updateVisibility();
}, true);
doc.addEventListener('focusout', function(event) {
window.setTimeout(function() {
if (!doc.hasFocus()) {
self.focusByKeyboard_ = true;
self.updateVisibility();
}
}, 0);
});
this.updateVisibility();
}
FocusOutlineManager.prototype = {
/**
* Whether focus change is triggered by TAB key.
* @type {boolean}
* @private
*/
focusByKeyboard_: true,
updateVisibility: function() {
this.visible = this.focusByKeyboard_;
},
/**
* Whether the focus outline should be visible.
* @type {boolean}
*/
set visible(visible) {
this.classList_.toggle(CLASS_NAME, visible);
},
get visible() {
return this.classList_.contains(CLASS_NAME);
}
};
/**
* Array of Document and FocusOutlineManager pairs.
* @type {Array}
*/
var docsToManager = [];
/**
* Gets a per document singleton focus outline manager.
* @param {Document} doc The document to get the |FocusOutlineManager| for.
* @return {cr.ui.FocusOutlineManager} The per document singleton focus
* outline manager.
*/
FocusOutlineManager.forDocument = function(doc) {
for (var i = 0; i < docsToManager.length; i++) {
if (doc == docsToManager[i][0])
return docsToManager[i][1];
}
var manager = new FocusOutlineManager(doc);
docsToManager.push([doc, manager]);
return manager;
};
return {FocusOutlineManager: FocusOutlineManager};
});

527
chrome/gpu_internals.js Normal file
View File

@ -0,0 +1,527 @@
// Copyright (c) 2011 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.
// // Copyright (c) 2011 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.
cr.define('gpu', function() {
/**
* This class provides a 'bridge' for communicating between javascript and the
* browser. When run outside of WebUI, e.g. as a regular webpage, it provides
* synthetic data to assist in testing.
* @constructor
*/
function BrowserBridge() {
// If we are not running inside WebUI, output chrome.send messages
// to the console to help with quick-iteration debugging.
this.debugMode_ = (chrome.send === undefined && console.log);
if (this.debugMode_) {
var browserBridgeTests = document.createElement('script');
browserBridgeTests.src = 'browser_bridge_tests.js';
document.body.appendChild(browserBridgeTests);
}
this.nextRequestId_ = 0;
this.pendingCallbacks_ = [];
this.logMessages_ = [];
// Tell c++ code that we are ready to receive GPU Info.
if (!this.debugMode_) {
chrome.send('browserBridgeInitialized');
this.beginRequestClientInfo_();
this.beginRequestLogMessages_();
}
}
BrowserBridge.prototype = {
__proto__: cr.EventTarget.prototype,
applySimulatedData_: function applySimulatedData(data) {
// set up things according to the simulated data
this.gpuInfo_ = data.gpuInfo;
this.clientInfo_ = data.clientInfo;
this.logMessages_ = data.logMessages;
cr.dispatchSimpleEvent(this, 'gpuInfoUpdate');
cr.dispatchSimpleEvent(this, 'clientInfoChange');
cr.dispatchSimpleEvent(this, 'logMessagesChange');
},
/**
* Returns true if the page is hosted inside Chrome WebUI
* Helps have behavior conditional to emulate_webui.py
*/
get debugMode() {
return this.debugMode_;
},
/**
* Sends a message to the browser with specified args. The
* browser will reply asynchronously via the provided callback.
*/
callAsync: function(submessage, args, callback) {
var requestId = this.nextRequestId_;
this.nextRequestId_ += 1;
this.pendingCallbacks_[requestId] = callback;
if (!args) {
chrome.send('callAsync', [requestId.toString(), submessage]);
} else {
var allArgs = [requestId.toString(), submessage].concat(args);
chrome.send('callAsync', allArgs);
}
},
/**
* Called by gpu c++ code when client info is ready.
*/
onCallAsyncReply: function(requestId, args) {
if (this.pendingCallbacks_[requestId] === undefined) {
throw new Error('requestId ' + requestId + ' is not pending');
}
var callback = this.pendingCallbacks_[requestId];
callback(args);
delete this.pendingCallbacks_[requestId];
},
/**
* Get gpuInfo data.
*/
get gpuInfo() {
return this.gpuInfo_;
},
/**
* Called from gpu c++ code when GPU Info is updated.
*/
onGpuInfoUpdate: function(gpuInfo) {
this.gpuInfo_ = gpuInfo;
cr.dispatchSimpleEvent(this, 'gpuInfoUpdate');
},
/**
* This function begins a request for the ClientInfo. If it comes back
* as undefined, then we will issue the request again in 250ms.
*/
beginRequestClientInfo_: function() {
this.callAsync('requestClientInfo', undefined, (function(data) {
if (data === undefined) { // try again in 250 ms
window.setTimeout(this.beginRequestClientInfo_.bind(this), 250);
} else {
this.clientInfo_ = data;
cr.dispatchSimpleEvent(this, 'clientInfoChange');
}
}).bind(this));
},
/**
* Returns information about the currently running Chrome build.
*/
get clientInfo() {
return this.clientInfo_;
},
/**
* This function checks for new GPU_LOG messages.
* If any are found, a refresh is triggered.
*/
beginRequestLogMessages_: function() {
this.callAsync('requestLogMessages', undefined,
(function(messages) {
if (messages.length != this.logMessages_.length) {
this.logMessages_ = messages;
cr.dispatchSimpleEvent(this, 'logMessagesChange');
}
// check again in 250 ms
window.setTimeout(this.beginRequestLogMessages_.bind(this), 250);
}).bind(this));
},
/**
* Returns an array of log messages issued by the GPU process, if any.
*/
get logMessages() {
return this.logMessages_;
},
/**
* Returns the value of the "Sandboxed" row.
*/
isSandboxedForTesting : function() {
for (i = 0; i < this.gpuInfo_.basic_info.length; ++i) {
var info = this.gpuInfo_.basic_info[i];
if (info.description == "Sandboxed")
return info.value;
}
return false;
}
};
return {
BrowserBridge: BrowserBridge
};
});
// // 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.
/**
* @fileoverview This view displays information on the current GPU
* hardware. Its primary usefulness is to allow users to copy-paste
* their data in an easy to read format for bug reports.
*/
cr.define('gpu', function() {
/**
* Provides information on the GPU process and underlying graphics hardware.
* @constructor
* @extends {cr.ui.TabPanel}
*/
var InfoView = cr.ui.define(cr.ui.TabPanel);
InfoView.prototype = {
__proto__: cr.ui.TabPanel.prototype,
decorate: function() {
cr.ui.TabPanel.prototype.decorate.apply(this);
browserBridge.addEventListener('gpuInfoUpdate', this.refresh.bind(this));
browserBridge.addEventListener('logMessagesChange',
this.refresh.bind(this));
browserBridge.addEventListener('clientInfoChange',
this.refresh.bind(this));
this.refresh();
},
/**
* Updates the view based on its currently known data
*/
refresh: function(data) {
// Client info
if (browserBridge.clientInfo) {
var clientInfo = browserBridge.clientInfo;
this.setTable_('client-info', [
{
description: 'Data exported',
value: (new Date()).toLocaleString()
},
{
description: 'Chrome version',
value: clientInfo.version
},
{
description: 'Operating system',
value: clientInfo.operating_system
},
{
description: 'Software rendering list version',
value: clientInfo.blacklist_version
},
{
description: 'Driver bug list version',
value: clientInfo.driver_bug_list_version
},
{
description: 'ANGLE commit id',
value: clientInfo.angle_commit_id
},
{
description: '2D graphics backend',
value: clientInfo.graphics_backend
},
{
description: 'Command Line',
value: clientInfo.command_line
}]);
} else {
this.setText_('client-info', '... loading...');
}
// Feature map
var featureLabelMap = {
'2d_canvas': 'Canvas',
'gpu_compositing': 'Compositing',
'webgl': 'WebGL',
'multisampling': 'WebGL multisampling',
'flash_3d': 'Flash',
'flash_stage3d': 'Flash Stage3D',
'flash_stage3d_baseline': 'Flash Stage3D Baseline profile',
'texture_sharing': 'Texture Sharing',
'video_decode': 'Video Decode',
'video_encode': 'Video Encode',
'panel_fitting': 'Panel Fitting',
'rasterization': 'Rasterization',
'multiple_raster_threads': 'Multiple Raster Threads',
'native_gpu_memory_buffers': 'Native GpuMemoryBuffers',
'vpx_decode': 'VPx Video Decode',
'webgl2': 'WebGL2',
'checker_imaging': 'CheckerImaging',
};
var statusMap = {
'disabled_software': {
'label': 'Software only. Hardware acceleration disabled',
'class': 'feature-yellow'
},
'disabled_off': {
'label': 'Disabled',
'class': 'feature-red'
},
'disabled_off_ok': {
'label': 'Disabled',
'class': 'feature-yellow'
},
'unavailable_software': {
'label': 'Software only, hardware acceleration unavailable',
'class': 'feature-yellow'
},
'unavailable_off': {
'label': 'Unavailable',
'class': 'feature-red'
},
'unavailable_off_ok': {
'label': 'Unavailable',
'class': 'feature-yellow'
},
'enabled_readback': {
'label': 'Hardware accelerated but at reduced performance',
'class': 'feature-yellow'
},
'enabled_force': {
'label': 'Hardware accelerated on all pages',
'class': 'feature-green'
},
'enabled': {
'label': 'Hardware accelerated',
'class': 'feature-green'
},
'enabled_on': {
'label': 'Enabled',
'class': 'feature-green'
},
'enabled_force_on': {
'label': 'Force enabled',
'class': 'feature-green'
},
};
// GPU info, basic
var diagnosticsDiv = this.querySelector('.diagnostics');
var diagnosticsLoadingDiv = this.querySelector('.diagnostics-loading');
var featureStatusList = this.querySelector('.feature-status-list');
var problemsDiv = this.querySelector('.problems-div');
var problemsList = this.querySelector('.problems-list');
var workaroundsDiv = this.querySelector('.workarounds-div');
var workaroundsList = this.querySelector('.workarounds-list');
var gpuInfo = browserBridge.gpuInfo;
var i;
if (gpuInfo) {
// Not using jstemplate here for blacklist status because we construct
// href from data, which jstemplate can't seem to do.
if (gpuInfo.featureStatus) {
// feature status list
featureStatusList.textContent = '';
for (var featureName in gpuInfo.featureStatus.featureStatus) {
var featureStatus =
gpuInfo.featureStatus.featureStatus[featureName];
var featureEl = document.createElement('li');
var nameEl = document.createElement('span');
if (!featureLabelMap[featureName])
console.log('Missing featureLabel for', featureName);
nameEl.textContent = featureLabelMap[featureName] + ': ';
featureEl.appendChild(nameEl);
var statusEl = document.createElement('span');
var statusInfo = statusMap[featureStatus];
if (!statusInfo) {
console.log('Missing status for ', featureStatus);
statusEl.textContent = 'Unknown';
statusEl.className = 'feature-red';
} else {
statusEl.textContent = statusInfo['label'];
statusEl.className = statusInfo['class'];
}
featureEl.appendChild(statusEl);
featureStatusList.appendChild(featureEl);
}
// problems list
if (gpuInfo.featureStatus.problems.length) {
problemsDiv.hidden = false;
problemsList.textContent = '';
for (i = 0; i < gpuInfo.featureStatus.problems.length; i++) {
var problem = gpuInfo.featureStatus.problems[i];
var problemEl = this.createProblemEl_(problem);
problemsList.appendChild(problemEl);
}
} else {
problemsDiv.hidden = true;
}
// driver bug workarounds list
if (gpuInfo.featureStatus.workarounds.length) {
workaroundsDiv.hidden = false;
workaroundsList.textContent = '';
for (i = 0; i < gpuInfo.featureStatus.workarounds.length; i++) {
var workaroundEl = document.createElement('li');
workaroundEl.textContent = gpuInfo.featureStatus.workarounds[i];
workaroundsList.appendChild(workaroundEl);
}
} else {
workaroundsDiv.hidden = true;
}
} else {
featureStatusList.textContent = '';
problemsList.hidden = true;
workaroundsList.hidden = true;
}
if (gpuInfo.basic_info)
this.setTable_('basic-info', gpuInfo.basic_info);
else
this.setTable_('basic-info', []);
if (gpuInfo.compositorInfo)
this.setTable_('compositor-info', gpuInfo.compositorInfo);
else
this.setTable_('compositor-info', []);
if (gpuInfo.gpuMemoryBufferInfo)
this.setTable_('gpu-memory-buffer-info', gpuInfo.gpuMemoryBufferInfo);
else
this.setTable_('gpu-memory-buffer-info', []);
if (gpuInfo.diagnostics) {
diagnosticsDiv.hidden = false;
diagnosticsLoadingDiv.hidden = true;
$('diagnostics-table').hidden = false;
this.setTable_('diagnostics-table', gpuInfo.diagnostics);
} else if (gpuInfo.diagnostics === null) {
// gpu_internals.cc sets diagnostics to null when it is being loaded
diagnosticsDiv.hidden = false;
diagnosticsLoadingDiv.hidden = false;
$('diagnostics-table').hidden = true;
} else {
diagnosticsDiv.hidden = true;
}
} else {
this.setText_('basic-info', '... loading ...');
diagnosticsDiv.hidden = true;
featureStatusList.textContent = '';
problemsDiv.hidden = true;
}
// Log messages
jstProcess(new JsEvalContext({values: browserBridge.logMessages}),
$('log-messages'));
},
createProblemEl_: function(problem) {
var problemEl;
problemEl = document.createElement('li');
// Description of issue
var desc = document.createElement('a');
desc.textContent = problem.description;
problemEl.appendChild(desc);
// Spacing ':' element
if (problem.crBugs.length > 0) {
var tmp = document.createElement('span');
tmp.textContent = ': ';
problemEl.appendChild(tmp);
}
var nbugs = 0;
var j;
// crBugs
for (j = 0; j < problem.crBugs.length; ++j) {
if (nbugs > 0) {
var tmp = document.createElement('span');
tmp.textContent = ', ';
problemEl.appendChild(tmp);
}
var link = document.createElement('a');
var bugid = parseInt(problem.crBugs[j]);
link.textContent = bugid;
link.href = 'http://crbug.com/' + bugid;
problemEl.appendChild(link);
nbugs++;
}
if (problem.affectedGpuSettings.length > 0) {
var brNode = document.createElement('br');
problemEl.appendChild(brNode);
var iNode = document.createElement('i');
problemEl.appendChild(iNode);
var headNode = document.createElement('span');
if (problem.tag == 'disabledFeatures')
headNode.textContent = 'Disabled Features: ';
else // problem.tag == 'workarounds'
headNode.textContent = 'Applied Workarounds: ';
iNode.appendChild(headNode);
for (j = 0; j < problem.affectedGpuSettings.length; ++j) {
if (j > 0) {
var separateNode = document.createElement('span');
separateNode.textContent = ', ';
iNode.appendChild(separateNode);
}
var nameNode = document.createElement('span');
if (problem.tag == 'disabledFeatures')
nameNode.classList.add('feature-red');
else // problem.tag == 'workarounds'
nameNode.classList.add('feature-yellow');
nameNode.textContent = problem.affectedGpuSettings[j];
iNode.appendChild(nameNode);
}
}
return problemEl;
},
setText_: function(outputElementId, text) {
var peg = document.getElementById(outputElementId);
peg.textContent = text;
},
setTable_: function(outputElementId, inputData) {
var template = jstGetTemplate('info-view-table-template');
jstProcess(new JsEvalContext({value: inputData}),
template);
var peg = document.getElementById(outputElementId);
if (!peg)
throw new Error('Node ' + outputElementId + ' not found');
peg.innerHTML = '';
peg.appendChild(template);
}
};
return {
InfoView: InfoView
};
});
var browserBridge;
/**
* Main entry point. called once the page has loaded.
*/
function onLoad() {
browserBridge = new gpu.BrowserBridge();
// Create the views.
cr.ui.decorate('#info-view', gpu.InfoView);
}
document.addEventListener('DOMContentLoaded', onLoad);

54
chrome/index.html Normal file
View File

@ -0,0 +1,54 @@
<!DOCTYPE html>
<html>
<head>
<title>check.wit.com</title>
<style>
body {
width: 100em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<br>
<br>
<br>
<link rel="stylesheet" href="tabs.css">
<link rel="stylesheet" href="widgets.css">
<script src="cr.js"></script>
<script src="event_target.js"></script>
<script src="ui.js"></script>
<script src="focus_outline_manager.js"></script>
<script src="tabs.js"></script>
<script src="load_time_data.js"></script>
<script src="util.js"></script>
<script src="gpu_internals.js"></script>
<script src="strings.js"></script>
<h2>All objects in cr</h2>
<table>
<script language="JavaScript" type="text/javascript">
for (var n in cr) {
document.write("<tr>");
document.write("<th align='left'>cr." + n + "<\/th>");
document.write("<th align='left'>" + cr[n] + "<\/th>");
document.write("<\/tr>");
}
</script>
</table>
<h2>All objects in browserBridge</h2>
<table>
<script language="JavaScript" type="text/javascript">
for (var n in browserBridge) {
document.write("<tr>");
document.write("<th align='left'>browserBridge." + n + "<\/th>");
document.write("<th align='left'>" + cr[n] + "<\/th>");
document.write("<\/tr>");
}
</script>
</table>

212
chrome/load_time_data.js Normal file
View File

@ -0,0 +1,212 @@
// 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.
/**
* @fileoverview This file defines a singleton which provides access to all data
* that is available as soon as the page's resources are loaded (before DOM
* content has finished loading). This data includes both localized strings and
* any data that is important to have ready from a very early stage (e.g. things
* that must be displayed right away).
*
* Note that loadTimeData is not guaranteed to be consistent between page
* refreshes (https://crbug.com/740629) and should not contain values that might
* change if the page is re-opened later.
*/
/** @type {!LoadTimeData} */ var loadTimeData;
// Expose this type globally as a temporary work around until
// https://github.com/google/closure-compiler/issues/544 is fixed.
/** @constructor */
function LoadTimeData(){}
(function() {
'use strict';
LoadTimeData.prototype = {
/**
* Sets the backing object.
*
* Note that there is no getter for |data_| to discourage abuse of the form:
*
* var value = loadTimeData.data()['key'];
*
* @param {Object} value The de-serialized page data.
*/
set data(value) {
expect(!this.data_, 'Re-setting data.');
this.data_ = value;
},
/**
* Returns a JsEvalContext for |data_|.
* @returns {JsEvalContext}
*/
createJsEvalContext: function() {
return new JsEvalContext(this.data_);
},
/**
* @param {string} id An ID of a value that might exist.
* @return {boolean} True if |id| is a key in the dictionary.
*/
valueExists: function(id) {
return id in this.data_;
},
/**
* Fetches a value, expecting that it exists.
* @param {string} id The key that identifies the desired value.
* @return {*} The corresponding value.
*/
getValue: function(id) {
expect(this.data_, 'No data. Did you remember to include strings.js?');
var value = this.data_[id];
expect(typeof value != 'undefined', 'Could not find value for ' + id);
return value;
},
/**
* As above, but also makes sure that the value is a string.
* @param {string} id The key that identifies the desired string.
* @return {string} The corresponding string value.
*/
getString: function(id) {
var value = this.getValue(id);
expectIsType(id, value, 'string');
return /** @type {string} */ (value);
},
/**
* Returns a formatted localized string where $1 to $9 are replaced by the
* second to the tenth argument.
* @param {string} id The ID of the string we want.
* @param {...(string|number)} var_args The extra values to include in the
* formatted output.
* @return {string} The formatted string.
*/
getStringF: function(id, var_args) {
var value = this.getString(id);
if (!value)
return '';
var args = Array.prototype.slice.call(arguments);
args[0] = value;
return this.substituteString.apply(this, args);
},
/**
* Returns a formatted localized string where $1 to $9 are replaced by the
* second to the tenth argument. Any standalone $ signs must be escaped as
* $$.
* @param {string} label The label to substitute through.
* This is not an resource ID.
* @param {...(string|number)} var_args The extra values to include in the
* formatted output.
* @return {string} The formatted string.
*/
substituteString: function(label, var_args) {
var varArgs = arguments;
return label.replace(/\$(.|$|\n)/g, function(m) {
assert(m.match(/\$[$1-9]/), 'Unescaped $ found in localized string.');
return m == '$$' ? '$' : varArgs[m[1]];
});
},
/**
* Returns a formatted string where $1 to $9 are replaced by the second to
* tenth argument, split apart into a list of pieces describing how the
* substitution was performed. Any standalone $ signs must be escaped as $$.
* @param {string} label A localized string to substitute through.
* This is not an resource ID.
* @param {...(string|number)} var_args The extra values to include in the
* formatted output.
* @return {!Array<!{value: string, arg: (null|string)}>} The formatted
* string pieces.
*/
getSubstitutedStringPieces: function(label, var_args) {
var varArgs = arguments;
// Split the string by separately matching all occurrences of $1-9 and of
// non $1-9 pieces.
var pieces = (label.match(/(\$[1-9])|(([^$]|\$([^1-9]|$))+)/g) ||
[]).map(function(p) {
// Pieces that are not $1-9 should be returned after replacing $$
// with $.
if (!p.match(/^\$[1-9]$/)) {
assert(
(p.match(/\$/g) || []).length % 2 == 0,
'Unescaped $ found in localized string.');
return {value: p.replace(/\$\$/g, '$'), arg: null};
}
// Otherwise, return the substitution value.
return {value: varArgs[p[1]], arg: p};
});
return pieces;
},
/**
* As above, but also makes sure that the value is a boolean.
* @param {string} id The key that identifies the desired boolean.
* @return {boolean} The corresponding boolean value.
*/
getBoolean: function(id) {
var value = this.getValue(id);
expectIsType(id, value, 'boolean');
return /** @type {boolean} */ (value);
},
/**
* As above, but also makes sure that the value is an integer.
* @param {string} id The key that identifies the desired number.
* @return {number} The corresponding number value.
*/
getInteger: function(id) {
var value = this.getValue(id);
expectIsType(id, value, 'number');
expect(value == Math.floor(value), 'Number isn\'t integer: ' + value);
return /** @type {number} */ (value);
},
/**
* Override values in loadTimeData with the values found in |replacements|.
* @param {Object} replacements The dictionary object of keys to replace.
*/
overrideValues: function(replacements) {
expect(
typeof replacements == 'object',
'Replacements must be a dictionary object.');
for (var key in replacements) {
this.data_[key] = replacements[key];
}
}
};
/**
* Checks condition, displays error message if expectation fails.
* @param {*} condition The condition to check for truthiness.
* @param {string} message The message to display if the check fails.
*/
function expect(condition, message) {
if (!condition) {
console.error(
'Unexpected condition on ' + document.location.href + ': ' + message);
}
}
/**
* Checks that the given value has the given type.
* @param {string} id The id of the value (only used for error message).
* @param {*} value The value to check the type on.
* @param {string} type The type we expect |value| to be.
*/
function expectIsType(id, value, type) {
expect(
typeof value == type, '[' + value + '] (' + id + ') is not a ' + type);
}
expect(!loadTimeData, 'should only include this file once');
loadTimeData = new LoadTimeData;
})();

1
chrome/strings.js Normal file
View File

@ -0,0 +1 @@
loadTimeData.data = {"fontfamily":"Ubuntu, Arial, sans-serif","fontsize":"75%","language":"en","textdirection":"ltr"};

119
chrome/tabs.css Normal file
View File

@ -0,0 +1,119 @@
/* 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. */
tabbox {
-webkit-box-orient: vertical;
display: -webkit-box;
}
tabs {
-webkit-padding-start: 8px;
background: -webkit-linear-gradient(white, rgb(243, 243, 243));
border-bottom: 1px solid rgb(160, 160, 160);
display: -webkit-box;
margin: 0;
}
/* New users of tabs.css should add 'new-style-tabs' to the class list of any
* 'tabs' or 'tabpanels' elements.
*
* TODO(rfevang): Remove when all users are converted to the new style.
* (crbug.com/247772). */
tabs.new-style-tabs {
-webkit-padding-start: 9px;
background: #fbfbfb;
border-bottom: 1px solid #c8c8c8;
padding-top: 14px;
}
tabs > * {
-webkit-margin-start: 5px;
background: rgba(160, 160, 160, .3);
border: 1px solid rgba(160, 160, 160, .3);
border-bottom: 0;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
cursor: default;
display: block;
min-width: 4em;
padding: 2px 10px;
text-align: center;
transition: border-color 150ms, background-color 150ms;
}
tabs.new-style-tabs > * {
-webkit-margin-start: 0;
background: #fbfbfb;
border: 1px solid #fbfbfb;
border-bottom: 0;
border-radius: 0;
min-width: 0;
padding: 4px 9px 4px 10px;
transition: none;
}
tabs > :not([selected]) {
background: rgba(238, 238, 238, .3);
}
tabs.new-style-tabs > :not([selected]) {
background: #fbfbfb;
color: #646464;
}
tabs > :not([selected]):hover {
background: rgba(247, 247, 247, .3);
}
tabs.new-style-tabs > :not([selected]):hover {
background: #fbfbfb;
color: black;
}
tabs > [selected] {
background: white;
border-color: rgb(160, 160, 160);
margin-bottom: -1px;
position: relative;
transition: none;
z-index: 0;
}
tabs.new-style-tabs > [selected] {
background: #fbfbfb;
border-color: #c8c8c8;
font-weight: bold;
}
tabs:focus {
outline: none;
}
html.focus-outline-visible tabs:focus > [selected] {
outline: 5px auto -webkit-focus-ring-color;
outline-offset: -2px;
}
tabpanels {
-webkit-box-flex: 1;
background: white;
box-shadow: 2px 2px 5px rgba(0, 0, 0, .2);
display: -webkit-box;
padding: 5px 15px 0 15px;
}
tabpanels.new-style-tabs {
background: #fbfbfb;
box-shadow: none;
padding: 0 20px;
}
tabpanels > * {
-webkit-box-flex: 1;
display: none;
}
tabpanels > [selected] {
display: block;
}

241
chrome/tabs.js Normal file
View File

@ -0,0 +1,241 @@
// Copyright (c) 2011 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.
cr.define('cr.ui', function() {
/**
* Returns the TabBox for a Tab or a TabPanel.
* @param {Tab|TabPanel} el The tab or tabpanel element.
* @return {TabBox} The tab box if found.
*/
function getTabBox(el) {
return findAncestor(el, function(node) {
return node.tagName == 'TABBOX';
});
}
/**
* Returns whether an element is a tab related object.
* @param {HTMLElement} el The element whose tag is being checked
* @return {boolean} Whether the element is a tab related element.
*/
function isTabElement(el) {
return el.tagName == 'TAB' || el.tagName == 'TABPANEL';
}
/**
* Set hook for the selected property for Tab and TabPanel.
* This sets the selectedIndex on the parent TabBox.
* @param {boolean} newValue The new selected value
* @param {boolean} oldValue The old selected value. (This is ignored atm.)
* @this {Tab|TabPanel}
*/
function selectedSetHook(newValue, oldValue) {
var tabBox;
if (newValue && (tabBox = getTabBox(this)))
tabBox.selectedIndex = Array.prototype.indexOf.call(p.children, this);
}
/**
* Decorates all the children of an element.
* @this {HTMLElement}
*/
function decorateChildren() {
var map = {
TABBOX: TabBox,
TABS: Tabs,
TAB: Tab,
TABPANELS: TabPanels,
TABPANEL: TabPanel
};
Object.keys(map).forEach(function(tagName) {
var children = this.getElementsByTagName(tagName);
var constr = map[tagName];
for (var i = 0; child = children[i]; i++) {
cr.ui.decorate(child, constr);
}
}.bind(this));
}
/**
* Set hook for TabBox selectedIndex.
* @param {number} selectedIndex The new selected index.
* @this {TabBox}
*/
function selectedIndexSetHook(selectedIndex) {
var child, tabChild, element;
element = this.querySelector('tabs');
if (element) {
for (var i = 0; child = element.children[i]; i++) {
child.selected = i == selectedIndex;
}
}
element = this.querySelector('tabpanels');
if (element) {
for (var i = 0; child = element.children[i]; i++) {
child.selected = i == selectedIndex;
}
}
}
/**
* Creates a new tabbox element.
* @param {Object=} opt_propertyBag Optional properties.
* @constructor
* @extends {HTMLElement}
*/
var TabBox = cr.ui.define('tabbox');
TabBox.prototype = {
__proto__: HTMLElement.prototype,
decorate: function() {
decorateChildren.call(this);
this.addEventListener('selectedChange', this.handleSelectedChange_, true);
this.selectedIndex = 0;
},
/**
* Callback for when a Tab or TabPanel changes its selected property.
* @param {Event} e The property change event.
* @private
*/
handleSelectedChange_: function(e) {
var target = e.target;
if (e.newValue && isTabElement(target) && getTabBox(target) == this) {
var index =
Array.prototype.indexOf.call(target.parentElement.children, target);
this.selectedIndex = index;
}
},
selectedIndex_: -1
};
/**
* The index of the selected tab or -1 if no tab is selected.
* @type {number}
*/
cr.defineProperty(
TabBox, 'selectedIndex', cr.PropertyKind.JS_PROP, selectedIndexSetHook);
/**
* Creates a new tabs element.
* @param {string} opt_label The text label for the item.
* @constructor
* @extends {HTMLElement}
*/
var Tabs = cr.ui.define('tabs');
Tabs.prototype = {
__proto__: HTMLElement.prototype,
decorate: function() {
decorateChildren.call(this);
// Make the Tabs element focusable.
this.tabIndex = 0;
this.addEventListener('keydown', this.handleKeyDown_.bind(this));
// Get (and initializes a focus outline manager.
this.focusOutlineManager_ =
cr.ui.FocusOutlineManager.forDocument(this.ownerDocument);
},
/**
* Handle keydown to change the selected tab when the user presses the
* arrow keys.
* @param {Event} e The keyboard event.
* @private
*/
handleKeyDown_: function(e) {
var delta = 0;
switch (e.key) {
case 'ArrowLeft':
case 'ArrowUp':
delta = -1;
break;
case 'ArrowRight':
case 'ArrowDown':
delta = 1;
break;
}
if (!delta)
return;
var cs = this.ownerDocument.defaultView.getComputedStyle(this);
if (cs.direction == 'rtl')
delta *= -1;
var count = this.children.length;
var tabbox = getTabBox(this);
var index = tabbox.selectedIndex;
tabbox.selectedIndex = (index + delta + count) % count;
// Show focus outline since we used the keyboard.
this.focusOutlineManager_.visible = true;
}
};
/**
* Creates a new tab element.
* @param {string} opt_label The text label for the item.
* @constructor
* @extends {HTMLElement}
*/
var Tab = cr.ui.define('tab');
Tab.prototype = {
__proto__: HTMLElement.prototype,
decorate: function() {
var self = this;
this.addEventListener(cr.isMac ? 'click' : 'mousedown', function() {
self.selected = true;
});
}
};
/**
* Whether the tab is selected.
* @type {boolean}
*/
cr.defineProperty(Tab, 'selected', cr.PropertyKind.BOOL_ATTR);
/**
* Creates a new tabpanels element.
* @param {string} opt_label The text label for the item.
* @constructor
* @extends {HTMLElement}
*/
var TabPanels = cr.ui.define('tabpanels');
TabPanels.prototype = {
__proto__: HTMLElement.prototype,
decorate: decorateChildren
};
/**
* Creates a new tabpanel element.
* @param {string} opt_label The text label for the item.
* @constructor
* @extends {HTMLElement}
*/
var TabPanel = cr.ui.define('tabpanel');
TabPanel.prototype = {
__proto__: HTMLElement.prototype,
decorate: function() {}
};
/**
* Whether the tab is selected.
* @type {boolean}
*/
cr.defineProperty(TabPanel, 'selected', cr.PropertyKind.BOOL_ATTR);
return {
TabBox: TabBox,
Tabs: Tabs,
Tab: Tab,
TabPanels: TabPanels,
TabPanel: TabPanel
};
});

212
chrome/ui.js Normal file
View File

@ -0,0 +1,212 @@
// 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.
cr.define('cr.ui', function() {
/**
* Decorates elements as an instance of a class.
* @param {string|!Element} source The way to find the element(s) to decorate.
* If this is a string then {@code querySeletorAll} is used to find the
* elements to decorate.
* @param {!Function} constr The constructor to decorate with. The constr
* needs to have a {@code decorate} function.
*/
function decorate(source, constr) {
var elements;
if (typeof source == 'string')
elements = cr.doc.querySelectorAll(source);
else
elements = [source];
for (var i = 0, el; el = elements[i]; i++) {
if (!(el instanceof constr))
constr.decorate(el);
}
}
/**
* Helper function for creating new element for define.
*/
function createElementHelper(tagName, opt_bag) {
// Allow passing in ownerDocument to create in a different document.
var doc;
if (opt_bag && opt_bag.ownerDocument)
doc = opt_bag.ownerDocument;
else
doc = cr.doc;
return doc.createElement(tagName);
}
/**
* Creates the constructor for a UI element class.
*
* Usage:
* <pre>
* var List = cr.ui.define('list');
* List.prototype = {
* __proto__: HTMLUListElement.prototype,
* decorate: function() {
* ...
* },
* ...
* };
* </pre>
*
* @param {string|Function} tagNameOrFunction The tagName or
* function to use for newly created elements. If this is a function it
* needs to return a new element when called.
* @return {function(Object=):Element} The constructor function which takes
* an optional property bag. The function also has a static
* {@code decorate} method added to it.
*/
function define(tagNameOrFunction) {
var createFunction, tagName;
if (typeof tagNameOrFunction == 'function') {
createFunction = tagNameOrFunction;
tagName = '';
} else {
createFunction = createElementHelper;
tagName = tagNameOrFunction;
}
/**
* Creates a new UI element constructor.
* @param {Object=} opt_propertyBag Optional bag of properties to set on the
* object after created. The property {@code ownerDocument} is special
* cased and it allows you to create the element in a different
* document than the default.
* @constructor
*/
function f(opt_propertyBag) {
var el = createFunction(tagName, opt_propertyBag);
f.decorate(el);
for (var propertyName in opt_propertyBag) {
el[propertyName] = opt_propertyBag[propertyName];
}
return el;
}
/**
* Decorates an element as a UI element class.
* @param {!Element} el The element to decorate.
*/
f.decorate = function(el) {
el.__proto__ = f.prototype;
el.decorate();
};
return f;
}
/**
* Input elements do not grow and shrink with their content. This is a simple
* (and not very efficient) way of handling shrinking to content with support
* for min width and limited by the width of the parent element.
* @param {!HTMLElement} el The element to limit the width for.
* @param {!HTMLElement} parentEl The parent element that should limit the
* size.
* @param {number} min The minimum width.
* @param {number=} opt_scale Optional scale factor to apply to the width.
*/
function limitInputWidth(el, parentEl, min, opt_scale) {
// Needs a size larger than borders
el.style.width = '10px';
var doc = el.ownerDocument;
var win = doc.defaultView;
var computedStyle = win.getComputedStyle(el);
var parentComputedStyle = win.getComputedStyle(parentEl);
var rtl = computedStyle.direction == 'rtl';
// To get the max width we get the width of the treeItem minus the position
// of the input.
var inputRect = el.getBoundingClientRect(); // box-sizing
var parentRect = parentEl.getBoundingClientRect();
var startPos = rtl ? parentRect.right - inputRect.right :
inputRect.left - parentRect.left;
// Add up border and padding of the input.
var inner = parseInt(computedStyle.borderLeftWidth, 10) +
parseInt(computedStyle.paddingLeft, 10) +
parseInt(computedStyle.paddingRight, 10) +
parseInt(computedStyle.borderRightWidth, 10);
// We also need to subtract the padding of parent to prevent it to overflow.
var parentPadding = rtl ? parseInt(parentComputedStyle.paddingLeft, 10) :
parseInt(parentComputedStyle.paddingRight, 10);
var max = parentEl.clientWidth - startPos - inner - parentPadding;
if (opt_scale)
max *= opt_scale;
function limit() {
if (el.scrollWidth > max) {
el.style.width = max + 'px';
} else {
el.style.width = 0;
var sw = el.scrollWidth;
if (sw < min) {
el.style.width = min + 'px';
} else {
el.style.width = sw + 'px';
}
}
}
el.addEventListener('input', limit);
limit();
}
/**
* Takes a number and spits out a value CSS will be happy with. To avoid
* subpixel layout issues, the value is rounded to the nearest integral value.
* @param {number} pixels The number of pixels.
* @return {string} e.g. '16px'.
*/
function toCssPx(pixels) {
if (!window.isFinite(pixels))
console.error('Pixel value is not a number: ' + pixels);
return Math.round(pixels) + 'px';
}
/**
* Users complain they occasionaly use doubleclicks instead of clicks
* (http://crbug.com/140364). To fix it we freeze click handling for
* the doubleclick time interval.
* @param {MouseEvent} e Initial click event.
*/
function swallowDoubleClick(e) {
var doc = e.target.ownerDocument;
var counter = Math.min(1, e.detail);
function swallow(e) {
e.stopPropagation();
e.preventDefault();
}
function onclick(e) {
if (e.detail > counter) {
counter = e.detail;
// Swallow the click since it's a click inside the doubleclick timeout.
swallow(e);
} else {
// Stop tracking clicks and let regular handling.
doc.removeEventListener('dblclick', swallow, true);
doc.removeEventListener('click', onclick, true);
}
}
// The following 'click' event (if e.type == 'mouseup') mustn't be taken
// into account (it mustn't stop tracking clicks). Start event listening
// after zero timeout.
setTimeout(function() {
doc.addEventListener('click', onclick, true);
doc.addEventListener('dblclick', swallow, true);
}, 0);
}
return {
decorate: decorate,
define: define,
limitInputWidth: limitInputWidth,
toCssPx: toCssPx,
swallowDoubleClick: swallowDoubleClick
};
});

505
chrome/util.js Normal file
View File

@ -0,0 +1,505 @@
// 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.
// // Copyright (c) 2013 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.
/**
* @fileoverview Assertion support.
*/
/**
* Verify |condition| is truthy and return |condition| if so.
* @template T
* @param {T} condition A condition to check for truthiness. Note that this
* may be used to test whether a value is defined or not, and we don't want
* to force a cast to Boolean.
* @param {string=} opt_message A message to show on failure.
* @return {T} A non-null |condition|.
*/
function assert(condition, opt_message) {
if (!condition) {
var message = 'Assertion failed';
if (opt_message)
message = message + ': ' + opt_message;
var error = new Error(message);
var global = function() {
return this;
}();
if (global.traceAssertionsForTesting)
console.warn(error.stack);
throw error;
}
return condition;
}
/**
* Call this from places in the code that should never be reached.
*
* For example, handling all the values of enum with a switch() like this:
*
* function getValueFromEnum(enum) {
* switch (enum) {
* case ENUM_FIRST_OF_TWO:
* return first
* case ENUM_LAST_OF_TWO:
* return last;
* }
* assertNotReached();
* return document;
* }
*
* This code should only be hit in the case of serious programmer error or
* unexpected input.
*
* @param {string=} opt_message A message to show when this is hit.
*/
function assertNotReached(opt_message) {
assert(false, opt_message || 'Unreachable code hit');
}
/**
* @param {*} value The value to check.
* @param {function(new: T, ...)} type A user-defined constructor.
* @param {string=} opt_message A message to show when this is hit.
* @return {T}
* @template T
*/
function assertInstanceof(value, type, opt_message) {
// We don't use assert immediately here so that we avoid constructing an error
// message if we don't have to.
if (!(value instanceof type)) {
assertNotReached(
opt_message ||
'Value ' + value + ' is not a[n] ' + (type.name || typeof type));
}
return value;
}
/**
* Alias for document.getElementById. Found elements must be HTMLElements.
* @param {string} id The ID of the element to find.
* @return {HTMLElement} The found element or null if not found.
*/
function $(id) {
// Disable getElementById restriction here, since we are instructing other
// places to re-use the $() that is defined here.
// eslint-disable-next-line no-restricted-properties
var el = document.getElementById(id);
return el ? assertInstanceof(el, HTMLElement) : null;
}
// TODO(devlin): This should return SVGElement, but closure compiler is missing
// those externs.
/**
* Alias for document.getElementById. Found elements must be SVGElements.
* @param {string} id The ID of the element to find.
* @return {Element} The found element or null if not found.
*/
function getSVGElement(id) {
// Disable getElementById restriction here, since it is not suitable for SVG
// elements.
// eslint-disable-next-line no-restricted-properties
var el = document.getElementById(id);
return el ? assertInstanceof(el, Element) : null;
}
/**
* Add an accessible message to the page that will be announced to
* users who have spoken feedback on, but will be invisible to all
* other users. It's removed right away so it doesn't clutter the DOM.
* @param {string} msg The text to be pronounced.
*/
function announceAccessibleMessage(msg) {
var element = document.createElement('div');
element.setAttribute('aria-live', 'polite');
element.style.position = 'fixed';
element.style.left = '-9999px';
element.style.height = '0px';
element.innerText = msg;
document.body.appendChild(element);
window.setTimeout(function() {
document.body.removeChild(element);
}, 0);
}
/**
* Generates a CSS url string.
* @param {string} s The URL to generate the CSS url for.
* @return {string} The CSS url string.
*/
function url(s) {
// http://www.w3.org/TR/css3-values/#uris
// Parentheses, commas, whitespace characters, single quotes (') and double
// quotes (") appearing in a URI must be escaped with a backslash
var s2 = s.replace(/(\(|\)|\,|\s|\'|\"|\\)/g, '\\$1');
// WebKit has a bug when it comes to URLs that end with \
// https://bugs.webkit.org/show_bug.cgi?id=28885
if (/\\\\$/.test(s2)) {
// Add a space to work around the WebKit bug.
s2 += ' ';
}
return 'url("' + s2 + '")';
}
/**
* Parses query parameters from Location.
* @param {Location} location The URL to generate the CSS url for.
* @return {Object} Dictionary containing name value pairs for URL
*/
function parseQueryParams(location) {
var params = {};
var query = unescape(location.search.substring(1));
var vars = query.split('&');
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split('=');
params[pair[0]] = pair[1];
}
return params;
}
/**
* Creates a new URL by appending or replacing the given query key and value.
* Not supporting URL with username and password.
* @param {Location} location The original URL.
* @param {string} key The query parameter name.
* @param {string} value The query parameter value.
* @return {string} The constructed new URL.
*/
function setQueryParam(location, key, value) {
var query = parseQueryParams(location);
query[encodeURIComponent(key)] = encodeURIComponent(value);
var newQuery = '';
for (var q in query) {
newQuery += (newQuery ? '&' : '?') + q + '=' + query[q];
}
return location.origin + location.pathname + newQuery + location.hash;
}
/**
* @param {Node} el A node to search for ancestors with |className|.
* @param {string} className A class to search for.
* @return {Element} A node with class of |className| or null if none is found.
*/
function findAncestorByClass(el, className) {
return /** @type {Element} */ (findAncestor(el, function(el) {
return el.classList && el.classList.contains(className);
}));
}
/**
* Return the first ancestor for which the {@code predicate} returns true.
* @param {Node} node The node to check.
* @param {function(Node):boolean} predicate The function that tests the
* nodes.
* @return {Node} The found ancestor or null if not found.
*/
function findAncestor(node, predicate) {
var last = false;
while (node != null && !(last = predicate(node))) {
node = node.parentNode;
}
return last ? node : null;
}
function swapDomNodes(a, b) {
var afterA = a.nextSibling;
if (afterA == b) {
swapDomNodes(b, a);
return;
}
var aParent = a.parentNode;
b.parentNode.replaceChild(a, b);
aParent.insertBefore(b, afterA);
}
/**
* Disables text selection and dragging, with optional whitelist callbacks.
* @param {function(Event):boolean=} opt_allowSelectStart Unless this function
* is defined and returns true, the onselectionstart event will be
* surpressed.
* @param {function(Event):boolean=} opt_allowDragStart Unless this function
* is defined and returns true, the ondragstart event will be surpressed.
*/
function disableTextSelectAndDrag(opt_allowSelectStart, opt_allowDragStart) {
// Disable text selection.
document.onselectstart = function(e) {
if (!(opt_allowSelectStart && opt_allowSelectStart.call(this, e)))
e.preventDefault();
};
// Disable dragging.
document.ondragstart = function(e) {
if (!(opt_allowDragStart && opt_allowDragStart.call(this, e)))
e.preventDefault();
};
}
/**
* Check the directionality of the page.
* @return {boolean} True if Chrome is running an RTL UI.
*/
function isRTL() {
return document.documentElement.dir == 'rtl';
}
/**
* Get an element that's known to exist by its ID. We use this instead of just
* calling getElementById and not checking the result because this lets us
* satisfy the JSCompiler type system.
* @param {string} id The identifier name.
* @return {!HTMLElement} the Element.
*/
function getRequiredElement(id) {
return assertInstanceof(
$(id), HTMLElement, 'Missing required element: ' + id);
}
/**
* Query an element that's known to exist by a selector. We use this instead of
* just calling querySelector and not checking the result because this lets us
* satisfy the JSCompiler type system.
* @param {string} selectors CSS selectors to query the element.
* @param {(!Document|!DocumentFragment|!Element)=} opt_context An optional
* context object for querySelector.
* @return {!HTMLElement} the Element.
*/
function queryRequiredElement(selectors, opt_context) {
var element = (opt_context || document).querySelector(selectors);
return assertInstanceof(
element, HTMLElement, 'Missing required element: ' + selectors);
}
// Handle click on a link. If the link points to a chrome: or file: url, then
// call into the browser to do the navigation.
['click', 'auxclick'].forEach(function(eventName) {
document.addEventListener(eventName, function(e) {
if (e.button > 1)
return; // Ignore buttons other than left and middle.
if (e.defaultPrevented)
return;
var eventPath = e.path;
var anchor = null;
if (eventPath) {
for (var i = 0; i < eventPath.length; i++) {
var element = eventPath[i];
if (element.tagName === 'A' && element.href) {
anchor = element;
break;
}
}
}
// Fallback if Event.path is not available.
var el = e.target;
if (!anchor && el.nodeType == Node.ELEMENT_NODE &&
el.webkitMatchesSelector('A, A *')) {
while (el.tagName != 'A') {
el = el.parentElement;
}
anchor = el;
}
if (!anchor)
return;
anchor = /** @type {!HTMLAnchorElement} */ (anchor);
if ((anchor.protocol == 'file:' || anchor.protocol == 'about:') &&
(e.button == 0 || e.button == 1)) {
chrome.send('navigateToUrl', [
anchor.href, anchor.target, e.button, e.altKey, e.ctrlKey, e.metaKey,
e.shiftKey
]);
e.preventDefault();
}
});
});
/**
* Creates a new URL which is the old URL with a GET param of key=value.
* @param {string} url The base URL. There is not sanity checking on the URL so
* it must be passed in a proper format.
* @param {string} key The key of the param.
* @param {string} value The value of the param.
* @return {string} The new URL.
*/
function appendParam(url, key, value) {
var param = encodeURIComponent(key) + '=' + encodeURIComponent(value);
if (url.indexOf('?') == -1)
return url + '?' + param;
return url + '&' + param;
}
/**
* Creates an element of a specified type with a specified class name.
* @param {string} type The node type.
* @param {string} className The class name to use.
* @return {Element} The created element.
*/
function createElementWithClassName(type, className) {
var elm = document.createElement(type);
elm.className = className;
return elm;
}
/**
* transitionend does not always fire (e.g. when animation is aborted
* or when no paint happens during the animation). This function sets up
* a timer and emulate the event if it is not fired when the timer expires.
* @param {!HTMLElement} el The element to watch for transitionend.
* @param {number=} opt_timeOut The maximum wait time in milliseconds for the
* transitionend to happen. If not specified, it is fetched from |el|
* using the transitionDuration style value.
*/
function ensureTransitionEndEvent(el, opt_timeOut) {
if (opt_timeOut === undefined) {
var style = getComputedStyle(el);
opt_timeOut = parseFloat(style.transitionDuration) * 1000;
// Give an additional 50ms buffer for the animation to complete.
opt_timeOut += 50;
}
var fired = false;
el.addEventListener('transitionend', function f(e) {
el.removeEventListener('transitionend', f);
fired = true;
});
window.setTimeout(function() {
if (!fired)
cr.dispatchSimpleEvent(el, 'transitionend', true);
}, opt_timeOut);
}
/**
* Alias for document.scrollTop getter.
* @param {!HTMLDocument} doc The document node where information will be
* queried from.
* @return {number} The Y document scroll offset.
*/
function scrollTopForDocument(doc) {
return doc.documentElement.scrollTop || doc.body.scrollTop;
}
/**
* Alias for document.scrollTop setter.
* @param {!HTMLDocument} doc The document node where information will be
* queried from.
* @param {number} value The target Y scroll offset.
*/
function setScrollTopForDocument(doc, value) {
doc.documentElement.scrollTop = doc.body.scrollTop = value;
}
/**
* Alias for document.scrollLeft getter.
* @param {!HTMLDocument} doc The document node where information will be
* queried from.
* @return {number} The X document scroll offset.
*/
function scrollLeftForDocument(doc) {
return doc.documentElement.scrollLeft || doc.body.scrollLeft;
}
/**
* Alias for document.scrollLeft setter.
* @param {!HTMLDocument} doc The document node where information will be
* queried from.
* @param {number} value The target X scroll offset.
*/
function setScrollLeftForDocument(doc, value) {
doc.documentElement.scrollLeft = doc.body.scrollLeft = value;
}
/**
* Replaces '&', '<', '>', '"', and ''' characters with their HTML encoding.
* @param {string} original The original string.
* @return {string} The string with all the characters mentioned above replaced.
*/
function HTMLEscape(original) {
return original.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
/**
* Shortens the provided string (if necessary) to a string of length at most
* |maxLength|.
* @param {string} original The original string.
* @param {number} maxLength The maximum length allowed for the string.
* @return {string} The original string if its length does not exceed
* |maxLength|. Otherwise the first |maxLength| - 1 characters with '...'
* appended.
*/
function elide(original, maxLength) {
if (original.length <= maxLength)
return original;
return original.substring(0, maxLength - 1) + '\u2026';
}
/**
* Quote a string so it can be used in a regular expression.
* @param {string} str The source string.
* @return {string} The escaped string.
*/
function quoteString(str) {
return str.replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, '\\$1');
}
/**
* Calls |callback| and stops listening the first time any event in |eventNames|
* is triggered on |target|.
* @param {!EventTarget} target
* @param {!Array<string>|string} eventNames Array or space-delimited string of
* event names to listen to (e.g. 'click mousedown').
* @param {function(!Event)} callback Called at most once. The
* optional return value is passed on by the listener.
*/
function listenOnce(target, eventNames, callback) {
if (!Array.isArray(eventNames))
eventNames = eventNames.split(/ +/);
var removeAllAndCallCallback = function(event) {
eventNames.forEach(function(eventName) {
target.removeEventListener(eventName, removeAllAndCallCallback, false);
});
return callback(event);
};
eventNames.forEach(function(eventName) {
target.addEventListener(eventName, removeAllAndCallCallback, false);
});
}
// /* is_ios */
/**
* Helper to convert callback-based define() API to a promise-based API.
* @suppress {undefinedVars}
* @param {!Array<string>} moduleNames
* @return {!Promise}
*/
function importModules(moduleNames) {
return new Promise(function(resolve) {
define(moduleNames, function() {
resolve(Array.from(arguments));
});
});
}
/**
* @param {!Event} e
* @return {boolean} Whether a modifier key was down when processing |e|.
*/
function hasKeyModifiers(e) {
return !!(e.altKey || e.ctrlKey || e.metaKey || e.shiftKey);
}

281
chrome/widgets.css Normal file
View File

@ -0,0 +1,281 @@
/* 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. */
/* This file defines styles for form controls. The order of rule blocks is
* important as there are some rules with equal specificity that rely on order
* as a tiebreaker. These are marked with OVERRIDE. */
@import url(action_link.css);
/* Default state **************************************************************/
:-webkit-any(button,
input[type='button'],
input[type='submit']):not(.custom-appearance),
select,
input[type='checkbox'],
input[type='radio'] {
-webkit-appearance: none;
background-image: -webkit-linear-gradient(#ededed, #ededed 38%, #dedede);
border: 1px solid rgba(0, 0, 0, 0.25);
border-radius: 2px;
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.08),
inset 0 1px 2px rgba(255, 255, 255, 0.75);
color: #444;
font: inherit;
margin: 0 1px 0 0;
outline: none;
text-shadow: 0 1px 0 rgb(240, 240, 240);
user-select: none;
}
:-webkit-any(button,
input[type='button'],
input[type='submit']):not(.custom-appearance),
select {
min-height: 2em;
min-width: 4em;
padding-bottom: 1px;
padding-top: 1px;
}
:-webkit-any(button,
input[type='button'],
input[type='submit']):not(.custom-appearance) {
-webkit-padding-end: 10px;
-webkit-padding-start: 10px;
}
select {
-webkit-appearance: none;
-webkit-padding-end: 24px;
-webkit-padding-start: 10px;
/* OVERRIDE */
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAAICAQAAACxSAwfAAAAUklEQVQY02P4z0AMRGZGMaShwCisyhITmb8huMzfEhOxKvuvsGAh208Ik+3ngoX/FbBbClcIUcSAw21QhXxfIIrwKAMpfNsEUYRXGVCEFc6CQwBqq4CCCtU4VgAAAABJRU5ErkJggg==),
-webkit-linear-gradient(#ededed, #ededed 38%, #dedede);
background-position: right center;
background-repeat: no-repeat;
}
html[dir='rtl'] select {
background-position: center left;
}
input[type='checkbox'] {
height: 13px;
position: relative;
vertical-align: middle;
width: 13px;
}
input[type='radio'] {
/* OVERRIDE */
border-radius: 100%;
height: 15px;
position: relative;
vertical-align: middle;
width: 15px;
}
/* TODO(estade): add more types here? */
input[type='number'],
input[type='password'],
input[type='search'],
input[type='text'],
input[type='url'],
input:not([type]),
textarea {
border: 1px solid #bfbfbf;
border-radius: 2px;
box-sizing: border-box;
color: #444;
font: inherit;
margin: 0;
/* Use min-height to accommodate addditional padding for touch as needed. */
min-height: 2em;
outline: none;
padding: 3px;
}
input[type='search'] {
-webkit-appearance: textfield;
/* NOTE: Keep a relatively high min-width for this so we don't obscure the end
* of the default text in relatively spacious languages (i.e. German). */
min-width: 160px;
}
/* Checked ********************************************************************/
input[type='checkbox']:checked::before {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAALCAQAAAADpb+tAAAAaElEQVR4Xl3PIQoCQQCF4Y8JW42D1bDZ4iVEjDbxFpstYhC7eIVBZHkXFGw734sv/TqDQQ8Xb1udja/I8igeIm7Aygj2IpoKTGZnVRNxAHYi4iPiDlA9xX+aNQDFySziqDN6uSp6y7ofEMwZ05uUZRkAAAAASUVORK5CYII=);
background-size: 100% 100%;
content: '';
display: block;
height: 100%;
user-select: none;
width: 100%;
}
input[type='radio']:checked::before {
background-color: #666;
border-radius: 100%;
bottom: 3px;
content: '';
display: block;
left: 3px;
position: absolute;
right: 3px;
top: 3px;
}
/* Hover **********************************************************************/
:enabled:hover:-webkit-any(
select,
input[type='checkbox'],
input[type='radio'],
:-webkit-any(
button,
input[type='button'],
input[type='submit']):not(.custom-appearance)) {
background-image: -webkit-linear-gradient(#f0f0f0, #f0f0f0 38%, #e0e0e0);
border-color: rgba(0, 0, 0, 0.3);
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.12),
inset 0 1px 2px rgba(255, 255, 255, 0.95);
color: black;
}
:enabled:hover:-webkit-any(select) {
/* OVERRIDE */
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAAICAQAAACxSAwfAAAAUklEQVQY02P4z0AMRGZGMaShwCisyhITmb8huMzfEhOxKvuvsGAh208Ik+3ngoX/FbBbClcIUcSAw21QhXxfIIrwKAMpfNsEUYRXGVCEFc6CQwBqq4CCCtU4VgAAAABJRU5ErkJggg==),
-webkit-linear-gradient(#f0f0f0, #f0f0f0 38%, #e0e0e0);
}
/* Active *********************************************************************/
:enabled:active:-webkit-any(
select,
input[type='checkbox'],
input[type='radio'],
:-webkit-any(
button,
input[type='button'],
input[type='submit']):not(.custom-appearance)) {
background-image: -webkit-linear-gradient(#e7e7e7, #e7e7e7 38%, #d7d7d7);
box-shadow: none;
text-shadow: none;
}
:enabled:active:-webkit-any(select) {
/* OVERRIDE */
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAAICAQAAACxSAwfAAAAUklEQVQY02P4z0AMRGZGMaShwCisyhITmb8huMzfEhOxKvuvsGAh208Ik+3ngoX/FbBbClcIUcSAw21QhXxfIIrwKAMpfNsEUYRXGVCEFc6CQwBqq4CCCtU4VgAAAABJRU5ErkJggg==),
-webkit-linear-gradient(#e7e7e7, #e7e7e7 38%, #d7d7d7);
}
/* Disabled *******************************************************************/
:disabled:-webkit-any(
button,
input[type='button'],
input[type='submit']):not(.custom-appearance),
select:disabled {
background-image: -webkit-linear-gradient(#f1f1f1, #f1f1f1 38%, #e6e6e6);
border-color: rgba(80, 80, 80, 0.2);
box-shadow: 0 1px 0 rgba(80, 80, 80, 0.08),
inset 0 1px 2px rgba(255, 255, 255, 0.75);
color: #aaa;
}
select:disabled {
/* OVERRIDE */
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAAICAQAAACxSAwfAAAASklEQVQY02P4z0AMRGZGMaShwCisyhITG/4jw8RErMr+KyxYiFC0YOF/BeyWIikEKWLA4Ta4QogiPMpACt82QRThVQYUYYWz4BAAGr6Ii6kEPacAAAAASUVORK5CYII=),
-webkit-linear-gradient(#f1f1f1, #f1f1f1 38%, #e6e6e6);
}
input:disabled:-webkit-any([type='checkbox'],
[type='radio']) {
opacity: .75;
}
input:disabled:-webkit-any([type='password'],
[type='search'],
[type='text'],
[type='url'],
:not([type])) {
color: #999;
}
/* Focus **********************************************************************/
:enabled:focus:-webkit-any(
select,
input[type='checkbox'],
input[type='number'],
input[type='password'],
input[type='radio'],
input[type='search'],
input[type='text'],
input[type='url'],
input:not([type]),
:-webkit-any(
button,
input[type='button'],
input[type='submit']):not(.custom-appearance)) {
/* We use border color because it follows the border radius (unlike outline).
* This is particularly noticeable on mac. */
border-color: rgb(77, 144, 254);
outline: none;
/* OVERRIDE */
transition: border-color 200ms;
}
/* Checkbox/radio helpers ******************************************************
*
* .checkbox and .radio classes wrap labels. Checkboxes and radios should use
* these classes with the markup structure:
*
* <div class="checkbox">
* <label>
* <input type="checkbox">
* <span>
* </label>
* </div>
*/
:-webkit-any(.checkbox, .radio) label {
/* Don't expand horizontally: <http://crbug.com/112091>. */
align-items: center;
display: inline-flex;
padding-bottom: 7px;
padding-top: 7px;
user-select: none;
}
:-webkit-any(.checkbox, .radio) label input {
flex-shrink: 0;
}
:-webkit-any(.checkbox, .radio) label input ~ span {
-webkit-margin-start: 0.6em;
/* Make sure long spans wrap at the same horizontal position they start. */
display: block;
}
:-webkit-any(.checkbox, .radio) label:hover {
color: black;
}
label > input:disabled:-webkit-any([type='checkbox'], [type='radio']) ~ span {
color: #999;
}
extensionview {
display: inline-block;
height: 300px;
width: 300px;
}

View File

@ -65,7 +65,6 @@ console.log("Your screen resolution = " +
</script>
<a href="http://check.wit.com:1200/">Hugo minx demo</a><br/>
<a href="http://check.wit.com:1201/">Hugo ananka demo</a><br/>
@ -82,6 +81,7 @@ console.log("Your screen resolution = " +
<a href="https://www.whatsmybrowser.org/">Show your browser details</a><br/>
<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent">Parsing user agent</a><br/>
<a href="blank.html">blank page. remember to start here.</a><br/>
<a href="javascript1.html">java test1</a><br/>
<a href="javascript2.html">java test2</a><br/>
<a href="javascript3.html">java test3</a><br/>
@ -92,6 +92,12 @@ console.log("Your screen resolution = " +
<a href="https://www.bramstein.com/writing/detecting-system-fonts-without-flash.html">detecting fonts</a><br/>
<a href="https://github.com/bramstein/fontfaceobserver/">Font Face Observer</a><br/>
<a href="https://github.com/typekit/webfontloader">Web Font Loader</a><br/>
<a href="https://webglstats.com/">Web GL status (also tests the browser)</a><br/>
<a href="chrome://gpu">Chrome internal GPU details</a><br/>
<a href="chrome/">Chrome internal GPU details</a><br/>
<a href="about:support">Firefox support details</a><br/>
<a href=""></a><br/>
<br>
<FORM>
@ -101,11 +107,46 @@ console.log("Your screen resolution = " +
<br>
<a href="https://developer.mozilla.org/en-US/docs/Web/API/Window">Official Mozilla Window Object documentation</a><br/>
<a href="https://get.webgl.org/troubleshooting/">Official webgl troubleshooting site</a><br/>
<a href="https://get.webgl.org/">Official webgl troubleshooting site</a><br/>
<a href="official_webgl_troubleshooting.html">Official webgl troubleshooting site (local copy)</a><br/>
<br>
<h2>All cookies</h2>
<script language="JavaScript" type="text/javascript">
var theCookies = document.cookie.split(';');
for (var n in theCookies) {
document.write(theCookies[n] + "<BR>\n");
}
function createCookie(name,value,days) {
var expires = "";
if (days) {
var date = new Date();
date.setTime(date.getTime() + (days*24*60*60*1000));
expires = "; expires=" + date.toUTCString();
}
document.cookie = name + "=" + value + expires + "; path=/";
}
function readCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for(var i=0;i < ca.length;i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1,c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
}
return null;
}
function eraseCookie(name) {
createCookie(name,"",-1);
}
createCookie('check.wit.com','test',7);
</script>
<h2>All objects in window.navigator</h2>
<table>
<script language="JavaScript" type="text/javascript">
@ -118,7 +159,6 @@ console.log("Your screen resolution = " +
</script>
</table>
<h2>All objects in window.screen.orientation</h2>
<table>
<script language="JavaScript" type="text/javascript">