add blank page
add chrome gpu stuff (doesn't work) dump out browser cookies
This commit is contained in:
parent
72a5a63bb5
commit
de2ad5a02f
|
@ -0,0 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>check.wit.com</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -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;
|
||||
}
|
|
@ -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]);
|
||||
|
||||
})();
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
}();
|
|
@ -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};
|
||||
});
|
|
@ -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};
|
||||
});
|
|
@ -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);
|
|
@ -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>
|
|
@ -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;
|
||||
})();
|
|
@ -0,0 +1 @@
|
|||
loadTimeData.data = {"fontfamily":"Ubuntu, Arial, sans-serif","fontsize":"75%","language":"en","textdirection":"ltr"};
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
};
|
||||
});
|
|
@ -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
|
||||
};
|
||||
});
|
|
@ -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, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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">
|
||||
|
|
Loading…
Reference in New Issue