Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/third_party/closure/goog/debug/debugwindow.js
2868 views
1
// Copyright 2006 The Closure Library Authors. All Rights Reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS-IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
/**
16
* @fileoverview Definition of the DebugWindow class. Please minimize
17
* dependencies this file has on other closure classes as any dependency it
18
* takes won't be able to use the logging infrastructure.
19
*
20
*/
21
22
goog.provide('goog.debug.DebugWindow');
23
24
goog.require('goog.debug.HtmlFormatter');
25
goog.require('goog.debug.LogManager');
26
goog.require('goog.debug.Logger');
27
goog.require('goog.dom.safe');
28
goog.require('goog.html.SafeHtml');
29
goog.require('goog.html.SafeStyleSheet');
30
goog.require('goog.string.Const');
31
goog.require('goog.structs.CircularBuffer');
32
goog.require('goog.userAgent');
33
34
35
36
/**
37
* Provides a debug DebugWindow that is bound to the goog.debug.Logger.
38
* It handles log messages and writes them to the DebugWindow. This doesn't
39
* provide a lot of functionality that the old Gmail logging infrastructure
40
* provided like saving debug logs for exporting to the server. Now that we
41
* have an event-based logging infrastructure, we can encapsulate that
42
* functionality in a separate class.
43
*
44
* @constructor
45
* @param {string=} opt_identifier Identifier for this logging class.
46
* @param {string=} opt_prefix Prefix prepended to messages.
47
*/
48
goog.debug.DebugWindow = function(opt_identifier, opt_prefix) {
49
/**
50
* Identifier for this logging class
51
* @protected {string}
52
*/
53
this.identifier = opt_identifier || '';
54
55
/**
56
* Array used to buffer log output
57
* @protected {!Array<!goog.html.SafeHtml>}
58
*/
59
this.outputBuffer = [];
60
61
/**
62
* Optional prefix to be prepended to error strings
63
* @private {string}
64
*/
65
this.prefix_ = opt_prefix || '';
66
67
/**
68
* Buffer for saving the last 1000 messages
69
* @private {!goog.structs.CircularBuffer}
70
*/
71
this.savedMessages_ =
72
new goog.structs.CircularBuffer(goog.debug.DebugWindow.MAX_SAVED);
73
74
/**
75
* Save the publish handler so it can be removed
76
* @private {!Function}
77
*/
78
this.publishHandler_ = goog.bind(this.addLogRecord, this);
79
80
/**
81
* Formatter for formatted output
82
* @private {goog.debug.Formatter}
83
*/
84
this.formatter_ = new goog.debug.HtmlFormatter(this.prefix_);
85
86
/**
87
* Loggers that we shouldn't output
88
* @private {!Object}
89
*/
90
this.filteredLoggers_ = {};
91
92
// enable by default
93
this.setCapturing(true);
94
95
/**
96
* Whether we are currently enabled. When the DebugWindow is enabled, it tries
97
* to keep its window open. When it's disabled, it can still be capturing log
98
* output if, but it won't try to write them to the DebugWindow window until
99
* it's enabled.
100
* @private {boolean}
101
*/
102
this.enabled_ = goog.debug.DebugWindow.isEnabled(this.identifier);
103
104
// timer to save the DebugWindow's window position in a cookie
105
goog.global.setInterval(goog.bind(this.saveWindowPositionSize_, this), 7500);
106
};
107
108
109
/**
110
* Max number of messages to be saved
111
* @type {number}
112
*/
113
goog.debug.DebugWindow.MAX_SAVED = 500;
114
115
116
/**
117
* How long to keep the cookies for in milliseconds
118
* @type {number}
119
*/
120
goog.debug.DebugWindow.COOKIE_TIME = 30 * 24 * 60 * 60 * 1000; // 30-days
121
122
123
/**
124
* HTML string printed when the debug window opens
125
* @type {string}
126
* @protected
127
*/
128
goog.debug.DebugWindow.prototype.welcomeMessage = 'LOGGING';
129
130
131
/**
132
* Whether to force enable the window on a severe log.
133
* @type {boolean}
134
* @private
135
*/
136
goog.debug.DebugWindow.prototype.enableOnSevere_ = false;
137
138
139
/**
140
* Reference to debug window
141
* @type {Window}
142
* @protected
143
*/
144
goog.debug.DebugWindow.prototype.win = null;
145
146
147
/**
148
* In the process of opening the window
149
* @type {boolean}
150
* @private
151
*/
152
goog.debug.DebugWindow.prototype.winOpening_ = false;
153
154
155
/**
156
* Whether we are currently capturing logger output.
157
*
158
* @type {boolean}
159
* @private
160
*/
161
goog.debug.DebugWindow.prototype.isCapturing_ = false;
162
163
164
/**
165
* Whether we already showed an alert that the DebugWindow was blocked.
166
* @type {boolean}
167
* @private
168
*/
169
goog.debug.DebugWindow.showedBlockedAlert_ = false;
170
171
172
/**
173
* Reference to timeout used to buffer the output stream.
174
* @type {?number}
175
* @private
176
*/
177
goog.debug.DebugWindow.prototype.bufferTimeout_ = null;
178
179
180
/**
181
* Timestamp for the last time the log was written to.
182
* @protected {number}
183
*/
184
goog.debug.DebugWindow.prototype.lastCall = goog.now();
185
186
187
/**
188
* Sets the welcome message shown when the window is first opened or reset.
189
*
190
* @param {string} msg An HTML string.
191
*/
192
goog.debug.DebugWindow.prototype.setWelcomeMessage = function(msg) {
193
this.welcomeMessage = msg;
194
};
195
196
197
/**
198
* Initializes the debug window.
199
*/
200
goog.debug.DebugWindow.prototype.init = function() {
201
if (this.enabled_) {
202
this.openWindow_();
203
}
204
};
205
206
207
/**
208
* Whether the DebugWindow is enabled. When the DebugWindow is enabled, it
209
* tries to keep its window open and logs all messages to the window. When the
210
* DebugWindow is disabled, it stops logging messages to its window.
211
*
212
* @return {boolean} Whether the DebugWindow is enabled.
213
*/
214
goog.debug.DebugWindow.prototype.isEnabled = function() {
215
return this.enabled_;
216
};
217
218
219
/**
220
* Sets whether the DebugWindow is enabled. When the DebugWindow is enabled, it
221
* tries to keep its window open and log all messages to the window. When the
222
* DebugWindow is disabled, it stops logging messages to its window. The
223
* DebugWindow also saves this state to a cookie so that it's persisted across
224
* application refreshes.
225
* @param {boolean} enable Whether the DebugWindow is enabled.
226
*/
227
goog.debug.DebugWindow.prototype.setEnabled = function(enable) {
228
this.enabled_ = enable;
229
230
if (this.enabled_) {
231
this.openWindow_();
232
}
233
234
this.setCookie_('enabled', enable ? '1' : '0');
235
};
236
237
238
/**
239
* Sets whether the debug window should be force enabled when a severe log is
240
* encountered.
241
* @param {boolean} enableOnSevere Whether to enable on severe logs..
242
*/
243
goog.debug.DebugWindow.prototype.setForceEnableOnSevere = function(
244
enableOnSevere) {
245
this.enableOnSevere_ = enableOnSevere;
246
};
247
248
249
/**
250
* Whether we are currently capturing logger output.
251
* @return {boolean} whether we are currently capturing logger output.
252
*/
253
goog.debug.DebugWindow.prototype.isCapturing = function() {
254
return this.isCapturing_;
255
};
256
257
258
/**
259
* Sets whether we are currently capturing logger output.
260
* @param {boolean} capturing Whether to capture logger output.
261
*/
262
goog.debug.DebugWindow.prototype.setCapturing = function(capturing) {
263
if (capturing == this.isCapturing_) {
264
return;
265
}
266
this.isCapturing_ = capturing;
267
268
// attach or detach handler from the root logger
269
var rootLogger = goog.debug.LogManager.getRoot();
270
if (capturing) {
271
rootLogger.addHandler(this.publishHandler_);
272
} else {
273
rootLogger.removeHandler(this.publishHandler_);
274
}
275
};
276
277
278
/**
279
* Gets the formatter for outputting to the debug window. The default formatter
280
* is an instance of goog.debug.HtmlFormatter
281
* @return {goog.debug.Formatter} The formatter in use.
282
*/
283
goog.debug.DebugWindow.prototype.getFormatter = function() {
284
return this.formatter_;
285
};
286
287
288
/**
289
* Sets the formatter for outputting to the debug window.
290
* @param {goog.debug.Formatter} formatter The formatter to use.
291
*/
292
goog.debug.DebugWindow.prototype.setFormatter = function(formatter) {
293
this.formatter_ = formatter;
294
};
295
296
297
/**
298
* Adds a separator to the debug window.
299
*/
300
goog.debug.DebugWindow.prototype.addSeparator = function() {
301
this.write_(goog.html.SafeHtml.create('hr'));
302
};
303
304
305
/**
306
* @return {boolean} Whether there is an active window.
307
*/
308
goog.debug.DebugWindow.prototype.hasActiveWindow = function() {
309
return !!this.win && !this.win.closed;
310
};
311
312
313
/**
314
* Clears the contents of the debug window
315
* @protected
316
*/
317
goog.debug.DebugWindow.prototype.clear = function() {
318
this.savedMessages_.clear();
319
if (this.hasActiveWindow()) {
320
this.writeInitialDocument();
321
}
322
};
323
324
325
/**
326
* Adds a log record.
327
* @param {goog.debug.LogRecord} logRecord the LogRecord.
328
*/
329
goog.debug.DebugWindow.prototype.addLogRecord = function(logRecord) {
330
if (this.filteredLoggers_[logRecord.getLoggerName()]) {
331
return;
332
}
333
var html = this.formatter_.formatRecordAsHtml(logRecord);
334
this.write_(html);
335
if (this.enableOnSevere_ &&
336
logRecord.getLevel().value >= goog.debug.Logger.Level.SEVERE.value) {
337
this.setEnabled(true);
338
}
339
};
340
341
342
/**
343
* Writes a message to the log, possibly opening up the window if it's enabled,
344
* or saving it if it's disabled.
345
* @param {!goog.html.SafeHtml} html The HTML to write.
346
* @private
347
*/
348
goog.debug.DebugWindow.prototype.write_ = function(html) {
349
// If the logger is enabled, open window and write html message to log
350
// otherwise save it
351
if (this.enabled_) {
352
this.openWindow_();
353
this.savedMessages_.add(html);
354
this.writeToLog_(html);
355
} else {
356
this.savedMessages_.add(html);
357
}
358
};
359
360
361
/**
362
* Write to the buffer. If a message hasn't been sent for more than 750ms just
363
* write, otherwise delay for a minimum of 250ms.
364
* @param {!goog.html.SafeHtml} html HTML to post to the log.
365
* @private
366
*/
367
goog.debug.DebugWindow.prototype.writeToLog_ = function(html) {
368
this.outputBuffer.push(html);
369
goog.global.clearTimeout(this.bufferTimeout_);
370
371
if (goog.now() - this.lastCall > 750) {
372
this.writeBufferToLog();
373
} else {
374
this.bufferTimeout_ =
375
goog.global.setTimeout(goog.bind(this.writeBufferToLog, this), 250);
376
}
377
};
378
379
380
/**
381
* Write to the log and maybe scroll into view.
382
* @protected
383
*/
384
goog.debug.DebugWindow.prototype.writeBufferToLog = function() {
385
this.lastCall = goog.now();
386
if (this.hasActiveWindow()) {
387
var body = this.win.document.body;
388
var scroll =
389
body && body.scrollHeight - (body.scrollTop + body.clientHeight) <= 100;
390
391
goog.dom.safe.documentWrite(
392
this.win.document, goog.html.SafeHtml.concat(this.outputBuffer));
393
this.outputBuffer.length = 0;
394
395
if (scroll) {
396
this.win.scrollTo(0, 1000000);
397
}
398
}
399
};
400
401
402
/**
403
* Writes all saved messages to the DebugWindow.
404
* @protected
405
*/
406
goog.debug.DebugWindow.prototype.writeSavedMessages = function() {
407
var messages = this.savedMessages_.getValues();
408
for (var i = 0; i < messages.length; i++) {
409
this.writeToLog_(messages[i]);
410
}
411
};
412
413
414
/**
415
* Opens the debug window if it is not already referenced
416
* @private
417
*/
418
goog.debug.DebugWindow.prototype.openWindow_ = function() {
419
if (this.hasActiveWindow() || this.winOpening_) {
420
return;
421
}
422
423
var winpos = this.getCookie_('dbg', '0,0,800,500').split(',');
424
var x = Number(winpos[0]);
425
var y = Number(winpos[1]);
426
var w = Number(winpos[2]);
427
var h = Number(winpos[3]);
428
429
this.winOpening_ = true;
430
this.win = window.open(
431
'', this.getWindowName_(), 'width=' + w + ',height=' + h +
432
',toolbar=no,resizable=yes,' +
433
'scrollbars=yes,left=' + x + ',top=' + y + ',status=no,screenx=' + x +
434
',screeny=' + y);
435
436
if (!this.win) {
437
if (!goog.debug.DebugWindow.showedBlockedAlert_) {
438
// only show this once
439
alert('Logger popup was blocked');
440
goog.debug.DebugWindow.showedBlockedAlert_ = true;
441
}
442
}
443
444
this.winOpening_ = false;
445
446
if (this.win) {
447
this.writeInitialDocument();
448
}
449
};
450
451
452
/**
453
* Gets a valid window name for the debug window. Replaces invalid characters in
454
* IE.
455
* @return {string} Valid window name.
456
* @private
457
*/
458
goog.debug.DebugWindow.prototype.getWindowName_ = function() {
459
return goog.userAgent.IE ? this.identifier.replace(/[\s\-\.\,]/g, '_') :
460
this.identifier;
461
};
462
463
464
/**
465
* @return {!goog.html.SafeStyleSheet} The stylesheet, for inclusion in the
466
* initial HTML.
467
*/
468
goog.debug.DebugWindow.prototype.getStyleRules = function() {
469
return goog.html.SafeStyleSheet.fromConstant(
470
goog.string.Const.from(
471
'*{font:normal 14px monospace;}' +
472
'.dbg-sev{color:#F00}' +
473
'.dbg-w{color:#E92}' +
474
'.dbg-sh{background-color:#fd4;font-weight:bold;color:#000}' +
475
'.dbg-i{color:#666}' +
476
'.dbg-f{color:#999}' +
477
'.dbg-ev{color:#0A0}' +
478
'.dbg-m{color:#990}'));
479
};
480
481
482
/**
483
* Writes the initial HTML of the debug window.
484
* @protected
485
*/
486
goog.debug.DebugWindow.prototype.writeInitialDocument = function() {
487
if (!this.hasActiveWindow()) {
488
return;
489
}
490
491
this.win.document.open();
492
493
var div = goog.html.SafeHtml.create(
494
'div', {
495
'class': 'dbg-ev',
496
'style': goog.string.Const.from('text-align:center;')
497
},
498
goog.html.SafeHtml.concat(
499
this.welcomeMessage, goog.html.SafeHtml.BR,
500
goog.html.SafeHtml.create(
501
'small', {}, 'Logger: ' + this.identifier)));
502
var html = goog.html.SafeHtml.concat(
503
goog.html.SafeHtml.createStyle(this.getStyleRules()),
504
goog.html.SafeHtml.create('hr'), div, goog.html.SafeHtml.create('hr'));
505
506
this.writeToLog_(html);
507
this.writeSavedMessages();
508
};
509
510
511
/**
512
* Save persistent data (using cookies) for 1 month (cookie specific to this
513
* logger object).
514
* @param {string} key Data name.
515
* @param {string} value Data value.
516
* @private
517
*/
518
goog.debug.DebugWindow.prototype.setCookie_ = function(key, value) {
519
var fullKey = goog.debug.DebugWindow.getCookieKey_(this.identifier, key);
520
document.cookie = fullKey + '=' + encodeURIComponent(value) +
521
';path=/;expires=' +
522
(new Date(goog.now() + goog.debug.DebugWindow.COOKIE_TIME)).toUTCString();
523
};
524
525
526
/**
527
* Retrieve data (using cookies).
528
* @param {string} key Data name.
529
* @param {string=} opt_default Optional default value if cookie doesn't exist.
530
* @return {string} Cookie value.
531
* @private
532
*/
533
goog.debug.DebugWindow.prototype.getCookie_ = function(key, opt_default) {
534
return goog.debug.DebugWindow.getCookieValue_(
535
this.identifier, key, opt_default);
536
};
537
538
539
/**
540
* Creates a valid cookie key name which is scoped to the given identifier.
541
* Substitutes all occurrences of invalid cookie name characters (whitespace,
542
* ';', and '=') with '_', which is a valid and readable alternative.
543
* @see goog.net.Cookies#isValidName
544
* @see <a href="http://tools.ietf.org/html/rfc2109">RFC 2109</a>
545
* @param {string} identifier Identifier for logging class.
546
* @param {string} key Data name.
547
* @return {string} Cookie key name.
548
* @private
549
*/
550
goog.debug.DebugWindow.getCookieKey_ = function(identifier, key) {
551
var fullKey = key + identifier;
552
return fullKey.replace(/[;=\s]/g, '_');
553
};
554
555
556
/**
557
* Retrieve data (using cookies).
558
* @param {string} identifier Identifier for logging class.
559
* @param {string} key Data name.
560
* @param {string=} opt_default Optional default value if cookie doesn't exist.
561
* @return {string} Cookie value.
562
* @private
563
*/
564
goog.debug.DebugWindow.getCookieValue_ = function(
565
identifier, key, opt_default) {
566
var fullKey = goog.debug.DebugWindow.getCookieKey_(identifier, key);
567
var cookie = String(document.cookie);
568
var start = cookie.indexOf(fullKey + '=');
569
if (start != -1) {
570
var end = cookie.indexOf(';', start);
571
return decodeURIComponent(
572
cookie.substring(
573
start + fullKey.length + 1, end == -1 ? cookie.length : end));
574
} else {
575
return opt_default || '';
576
}
577
};
578
579
580
/**
581
* @param {string} identifier Identifier for logging class.
582
* @return {boolean} Whether the DebugWindow is enabled.
583
*/
584
goog.debug.DebugWindow.isEnabled = function(identifier) {
585
return goog.debug.DebugWindow.getCookieValue_(identifier, 'enabled') == '1';
586
};
587
588
589
/**
590
* Saves the window position size to a cookie
591
* @private
592
*/
593
goog.debug.DebugWindow.prototype.saveWindowPositionSize_ = function() {
594
if (!this.hasActiveWindow()) {
595
return;
596
}
597
var x = this.win.screenX || this.win.screenLeft || 0;
598
var y = this.win.screenY || this.win.screenTop || 0;
599
var w = this.win.outerWidth || 800;
600
var h = this.win.outerHeight || 500;
601
this.setCookie_('dbg', x + ',' + y + ',' + w + ',' + h);
602
};
603
604
605
/**
606
* Adds a logger name to be filtered.
607
* @param {string} loggerName the logger name to add.
608
*/
609
goog.debug.DebugWindow.prototype.addFilter = function(loggerName) {
610
this.filteredLoggers_[loggerName] = 1;
611
};
612
613
614
/**
615
* Removes a logger name to be filtered.
616
* @param {string} loggerName the logger name to remove.
617
*/
618
goog.debug.DebugWindow.prototype.removeFilter = function(loggerName) {
619
delete this.filteredLoggers_[loggerName];
620
};
621
622
623
/**
624
* Modify the size of the circular buffer. Allows the log to retain more
625
* information while the window is closed.
626
* @param {number} size New size of the circular buffer.
627
*/
628
goog.debug.DebugWindow.prototype.resetBufferWithNewSize = function(size) {
629
if (size > 0 && size < 50000) {
630
this.clear();
631
this.savedMessages_ = new goog.structs.CircularBuffer(size);
632
}
633
};
634
635