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>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<a href="http://check.wit.com:1200/">Hugo minx demo</a><br/>
|
<a href="http://check.wit.com:1200/">Hugo minx demo</a><br/>
|
||||||
<a href="http://check.wit.com:1201/">Hugo ananka 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://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="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="javascript1.html">java test1</a><br/>
|
||||||
<a href="javascript2.html">java test2</a><br/>
|
<a href="javascript2.html">java test2</a><br/>
|
||||||
<a href="javascript3.html">java test3</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://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/bramstein/fontfaceobserver/">Font Face Observer</a><br/>
|
||||||
<a href="https://github.com/typekit/webfontloader">Web Font Loader</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>
|
<br>
|
||||||
<FORM>
|
<FORM>
|
||||||
|
@ -101,11 +107,46 @@ console.log("Your screen resolution = " +
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
<a href="https://developer.mozilla.org/en-US/docs/Web/API/Window">Official Mozilla Window Object documentation</a><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/>
|
<a href="official_webgl_troubleshooting.html">Official webgl troubleshooting site (local copy)</a><br/>
|
||||||
<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>
|
<h2>All objects in window.navigator</h2>
|
||||||
<table>
|
<table>
|
||||||
<script language="JavaScript" type="text/javascript">
|
<script language="JavaScript" type="text/javascript">
|
||||||
|
@ -118,7 +159,6 @@ console.log("Your screen resolution = " +
|
||||||
</script>
|
</script>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
||||||
<h2>All objects in window.screen.orientation</h2>
|
<h2>All objects in window.screen.orientation</h2>
|
||||||
<table>
|
<table>
|
||||||
<script language="JavaScript" type="text/javascript">
|
<script language="JavaScript" type="text/javascript">
|
||||||
|
|
Loading…
Reference in New Issue