Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/javascript/atoms/window.js
2884 views
1
// Licensed to the Software Freedom Conservancy (SFC) under one
2
// or more contributor license agreements. See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership. The SFC licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License. You may obtain a copy of the License at
8
//
9
// http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied. See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
/**
19
* @fileoverview Atoms for simulating user actions against the browser window.
20
*/
21
22
goog.provide('bot.window');
23
24
goog.require('bot');
25
goog.require('bot.Error');
26
goog.require('bot.ErrorCode');
27
goog.require('bot.events');
28
goog.require('bot.userAgent');
29
goog.require('goog.dom');
30
goog.require('goog.dom.DomHelper');
31
goog.require('goog.math.Coordinate');
32
goog.require('goog.math.Size');
33
goog.require('goog.style');
34
goog.require('goog.userAgent');
35
goog.require('goog.userAgent.product');
36
37
38
/**
39
* Whether the value of history.length includes a newly loaded page. If not,
40
* after a new page load history.length is the number of pages that have loaded,
41
* minus 1, but becomes the total number of pages on a subsequent back() call.
42
* @private {boolean}
43
* @const
44
*/
45
bot.window.HISTORY_LENGTH_INCLUDES_NEW_PAGE_ = !goog.userAgent.IE;
46
47
48
/**
49
* Whether value of history.length includes the pages ahead of the current one
50
* in the history. If not, history.length equals the number of prior pages.
51
* Here is the WebKit bug for this behavior that was fixed by version 533:
52
* https://bugs.webkit.org/show_bug.cgi?id=24472
53
* @private {boolean}
54
* @const
55
*/
56
bot.window.HISTORY_LENGTH_INCLUDES_FORWARD_PAGES_ =
57
!goog.userAgent.WEBKIT || bot.userAgent.isEngineVersion('533');
58
59
60
/**
61
* Screen orientation values. From the draft W3C spec at:
62
* http://www.w3.org/TR/2012/WD-screen-orientation-20120522
63
*
64
* @enum {string}
65
*/
66
bot.window.Orientation = {
67
PORTRAIT: 'portrait-primary',
68
PORTRAIT_SECONDARY: 'portrait-secondary',
69
LANDSCAPE: 'landscape-primary',
70
LANDSCAPE_SECONDARY: 'landscape-secondary'
71
};
72
73
74
/**
75
* Returns the degrees corresponding to the orientation input.
76
*
77
* @param {!bot.window.Orientation} orientation The orientation.
78
* @return {number} The orientation degrees.
79
* @private
80
*/
81
bot.window.getOrientationDegrees_ = (function () {
82
var orientationMap;
83
return function (orientation) {
84
if (!orientationMap) {
85
orientationMap = {};
86
if (goog.userAgent.MOBILE) {
87
// The iPhone and Android phones do not change orientation event when
88
// held upside down. Hence, PORTRAIT_SECONDARY is not set.
89
orientationMap[bot.window.Orientation.PORTRAIT] = 0;
90
orientationMap[bot.window.Orientation.LANDSCAPE] = 90;
91
orientationMap[bot.window.Orientation.LANDSCAPE_SECONDARY] = -90;
92
if (goog.userAgent.product.IPAD) {
93
orientationMap[bot.window.Orientation.PORTRAIT_SECONDARY] = 180;
94
}
95
} else if (goog.userAgent.product.ANDROID) {
96
// Unlike the iPad, Android tablets treat landscape orientation as the
97
// default, i.e., having window.orientation = 0.
98
orientationMap[bot.window.Orientation.PORTRAIT] = -90;
99
orientationMap[bot.window.Orientation.LANDSCAPE] = 0;
100
orientationMap[bot.window.Orientation.PORTRAIT_SECONDARY] = 90;
101
orientationMap[bot.window.Orientation.LANDSCAPE_SECONDARY] = 180;
102
}
103
}
104
return orientationMap[orientation];
105
};
106
})();
107
108
109
/**
110
* Go back in the browser history. The number of pages to go back can
111
* optionally be specified and defaults to 1.
112
*
113
* @param {number=} opt_numPages Number of pages to go back.
114
*/
115
bot.window.back = function (opt_numPages) {
116
// Relax the upper bound by one for browsers that do not count
117
// newly loaded pages towards the value of window.history.length.
118
var maxPages = bot.window.HISTORY_LENGTH_INCLUDES_NEW_PAGE_ ?
119
bot.getWindow().history.length - 1 : bot.getWindow().history.length;
120
var numPages = bot.window.checkNumPages_(maxPages, opt_numPages);
121
bot.getWindow().history.go(-numPages);
122
};
123
124
125
/**
126
* Go forward in the browser history. The number of pages to go forward can
127
* optionally be specified and defaults to 1.
128
*
129
* @param {number=} opt_numPages Number of pages to go forward.
130
*/
131
bot.window.forward = function (opt_numPages) {
132
// Do not check the upper bound (use null for infinity) for browsers that
133
// do not count forward pages towards the value of window.history.length.
134
var maxPages = bot.window.HISTORY_LENGTH_INCLUDES_FORWARD_PAGES_ ?
135
bot.getWindow().history.length - 1 : null;
136
var numPages = bot.window.checkNumPages_(maxPages, opt_numPages);
137
bot.getWindow().history.go(numPages);
138
};
139
140
141
/**
142
* @param {?number} maxPages Upper bound on number of pages; null for infinity.
143
* @param {number=} opt_numPages Number of pages to move in history.
144
* @return {number} Correct number of pages to move in history.
145
* @private
146
*/
147
bot.window.checkNumPages_ = function (maxPages, opt_numPages) {
148
var numPages = goog.isDef(opt_numPages) ? opt_numPages : 1;
149
if (numPages <= 0) {
150
throw new bot.Error(bot.ErrorCode.UNKNOWN_ERROR,
151
'number of pages must be positive');
152
}
153
if (maxPages !== null && numPages > maxPages) {
154
throw new bot.Error(bot.ErrorCode.UNKNOWN_ERROR,
155
'number of pages must be less than the length of the browser history');
156
}
157
return numPages;
158
};
159
160
161
/**
162
* Determine the size of the window that a user could interact with. This will
163
* be the greatest of document.body.(width|scrollWidth), the same for
164
* document.documentElement or the size of the viewport.
165
*
166
* @param {!Window=} opt_win Window to determine the size of. Defaults to
167
* bot.getWindow().
168
* @return {!goog.math.Size} The calculated size.
169
*/
170
bot.window.getInteractableSize = function (opt_win) {
171
var win = opt_win || bot.getWindow();
172
var doc = win.document;
173
var elem = doc.documentElement;
174
var body = doc.body;
175
if (!body) {
176
throw new bot.Error(bot.ErrorCode.UNKNOWN_ERROR,
177
'No BODY element present');
178
}
179
180
var widths = [
181
elem.clientWidth, elem.scrollWidth, elem.offsetWidth,
182
body.scrollWidth, body.offsetWidth
183
];
184
var heights = [
185
elem.clientHeight, elem.scrollHeight, elem.offsetHeight,
186
body.scrollHeight, body.offsetHeight
187
];
188
189
var width = Math.max.apply(null, widths);
190
var height = Math.max.apply(null, heights);
191
192
return new goog.math.Size(width, height);
193
};
194
195
196
/**
197
* Gets the frame element.
198
*
199
* @param {!Window} win Window of the frame. Defaults to bot.getWindow().
200
* @return {Element} The frame element if it exists, null otherwise.
201
* @private
202
*/
203
bot.window.getFrame_ = function (win) {
204
try {
205
// On IE, accessing the frameElement of a popup window results in a "No
206
// Such interface" exception.
207
return win.frameElement;
208
} catch (e) {
209
return null;
210
}
211
};
212
213
214
/**
215
* Determine the outer size of the window.
216
*
217
* @param {!Window=} opt_win Window to determine the size of. Defaults to
218
* bot.getWindow().
219
* @return {!goog.math.Size} The calculated size.
220
*/
221
bot.window.getSize = function (opt_win) {
222
var win = opt_win || bot.getWindow();
223
var frame = bot.window.getFrame_(win);
224
if (bot.userAgent.ANDROID_PRE_ICECREAMSANDWICH) {
225
if (frame) {
226
// Early Android browsers do not account for border width.
227
var box = goog.style.getBorderBox(frame);
228
return new goog.math.Size(frame.clientWidth - box.left - box.right,
229
frame.clientHeight);
230
} else {
231
// A fixed popup size.
232
return new goog.math.Size(320, 240);
233
}
234
} else if (frame) {
235
return new goog.math.Size(frame.clientWidth, frame.clientHeight);
236
} else {
237
var docElem = win.document.documentElement;
238
var body = win.document.body;
239
var width = win.outerWidth || (docElem && docElem.clientWidth) ||
240
(body && body.clientWidth) || 0;
241
var height = win.outerHeight || (docElem && docElem.clientHeight) ||
242
(body && body.clientHeight) || 0;
243
return new goog.math.Size(width, height);
244
}
245
};
246
247
248
/**
249
* Set the outer size of the window.
250
*
251
* @param {!goog.math.Size} size The new window size.
252
* @param {!Window=} opt_win Window to determine the size of. Defaults to
253
* bot.getWindow().
254
*/
255
bot.window.setSize = function (size, opt_win) {
256
var win = opt_win || bot.getWindow();
257
var frame = bot.window.getFrame_(win);
258
if (frame) {
259
// minHeight and minWidth are altered because many browsers will not change
260
// height or width if it is less than a specified minHeight or minWidth.
261
frame.style.minHeight = '0px';
262
frame.style.minWidth = '0px';
263
frame.width = size.width + 'px';
264
frame.style.width = size.width + 'px';
265
frame.height = size.height + 'px';
266
frame.style.height = size.height + 'px';
267
} else {
268
win.resizeTo(size.width, size.height);
269
}
270
};
271
272
273
/**
274
* Determine the scroll position of the window.
275
*
276
* @param {!Window=} opt_win Window to determine the scroll position of.
277
* Defaults to bot.getWindow().
278
* @return {!goog.math.Coordinate} The scroll position.
279
*/
280
bot.window.getScroll = function (opt_win) {
281
var win = opt_win || bot.getWindow();
282
return new goog.dom.DomHelper(win.document).getDocumentScroll();
283
};
284
285
286
/**
287
* Set the scroll position of the window.
288
*
289
* @param {!goog.math.Coordinate} position The new scroll position.
290
* @param {!Window=} opt_win Window to apply position to. Defaults to
291
* bot.getWindow().
292
*/
293
bot.window.setScroll = function (position, opt_win) {
294
var win = opt_win || bot.getWindow();
295
win.scrollTo(position.x, position.y);
296
};
297
298
299
/**
300
* Get the position of the window.
301
*
302
* @param {!Window=} opt_win Window to determine the position of. Defaults to
303
* bot.getWindow().
304
* @return {!goog.math.Coordinate} The position of the window.
305
*/
306
bot.window.getPosition = function (opt_win) {
307
var win = opt_win || bot.getWindow();
308
var x, y;
309
310
if (goog.userAgent.IE) {
311
x = win.screenLeft;
312
y = win.screenTop;
313
} else {
314
x = win.screenX;
315
y = win.screenY;
316
}
317
318
return new goog.math.Coordinate(x, y);
319
};
320
321
322
/**
323
* Set the position of the window.
324
*
325
* @param {!goog.math.Coordinate} position The target position.
326
* @param {!Window=} opt_win Window to set the position of. Defaults to
327
* bot.getWindow().
328
*/
329
bot.window.setPosition = function (position, opt_win) {
330
var win = opt_win || bot.getWindow();
331
win.moveTo(position.x, position.y);
332
};
333
334
335
/**
336
* Scrolls the given position into the viewport, using the minimal amount of
337
* scrolling necessary to being the coordinate into view.
338
*
339
* @param {!goog.math.Coordinate} position The position to scroll into view.
340
* @param {!Window=} opt_win Window to apply position to. Defaults to
341
* bot.getWindow().
342
*/
343
bot.window.scrollIntoView = function (position, opt_win) {
344
var win = opt_win || bot.getWindow();
345
var viewport = goog.dom.getViewportSize(win);
346
var scroll = bot.window.getScroll(win);
347
348
// Scroll the minimal amount to bring the position into view.
349
var targetScroll = new goog.math.Coordinate(
350
newScrollDim(position.x, scroll.x, viewport.width),
351
newScrollDim(position.y, scroll.y, viewport.height));
352
if (!goog.math.Coordinate.equals(targetScroll, scroll)) {
353
bot.window.setScroll(targetScroll, win);
354
}
355
356
// It is difficult to determine the size of the web page in some browsers.
357
// We check if the scrolling we intended to do really happened. If not we
358
// assume that the target location is not on the web page.
359
if (!goog.math.Coordinate.equals(targetScroll, bot.window.getScroll(win))) {
360
throw new bot.Error(bot.ErrorCode.MOVE_TARGET_OUT_OF_BOUNDS,
361
'The target scroll location ' + targetScroll + ' is not on the page.');
362
}
363
364
function newScrollDim(positionDim, scrollDim, viewportDim) {
365
if (positionDim < scrollDim) {
366
return positionDim;
367
} else if (positionDim >= scrollDim + viewportDim) {
368
return positionDim - viewportDim + 1;
369
} else {
370
return scrollDim;
371
}
372
}
373
};
374
375
376
/**
377
* @return {number} The current window orientation degrees.
378
* window.
379
* @private
380
*/
381
bot.window.getCurrentOrientationDegrees_ = function () {
382
var win = bot.getWindow();
383
if (!goog.isDef(win.orientation)) {
384
// If window.orientation is not defined, assume a default orientation of 0.
385
// A value of 0 indicates a portrait orientation except for android tablets
386
// where 0 indicates a landscape orientation.
387
win.orientation = 0;
388
}
389
return win.orientation;
390
};
391
392
393
/**
394
* Changes window orientation.
395
*
396
* @param {!bot.window.Orientation} orientation The new orientation of the
397
* window.
398
*/
399
bot.window.changeOrientation = function (orientation) {
400
var win = bot.getWindow();
401
var currentOrientationDegrees = bot.window.getCurrentOrientationDegrees_();
402
var newOrientationDegrees = bot.window.getOrientationDegrees_(orientation);
403
if (currentOrientationDegrees == newOrientationDegrees ||
404
!goog.isDef(newOrientationDegrees)) {
405
return;
406
}
407
408
// If possible, try to override the window's orientation value.
409
// On some older version of Android, it's not possible to change
410
// the window's orientation value.
411
if (Object.getOwnPropertyDescriptor && Object.defineProperty) {
412
var descriptor = Object.getOwnPropertyDescriptor(win, 'orientation');
413
if (descriptor && descriptor.configurable) {
414
Object.defineProperty(win, 'orientation', {
415
configurable: true,
416
get: function () {
417
return newOrientationDegrees;
418
}
419
});
420
}
421
}
422
bot.events.fire(win, bot.events.EventType.ORIENTATIONCHANGE);
423
424
// Change the window size to reflect the new orientation.
425
if (Math.abs(currentOrientationDegrees - newOrientationDegrees) % 180 != 0) {
426
var size = bot.window.getSize();
427
var shorter = size.getShortest();
428
var longer = size.getLongest();
429
if (orientation == bot.window.Orientation.PORTRAIT ||
430
orientation == bot.window.Orientation.PORTRAIT_SECONDARY) {
431
bot.window.setSize(new goog.math.Size(shorter, longer));
432
} else {
433
bot.window.setSize(new goog.math.Size(longer, shorter));
434
}
435
}
436
};
437
438