Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/third_party/closure/goog/cssom/cssom.js
2868 views
1
// Copyright 2008 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 CSS Object Model helper functions.
17
* References:
18
* - W3C: http://dev.w3.org/csswg/cssom/
19
* - MSDN: http://msdn.microsoft.com/en-us/library/ms531209(VS.85).aspx.
20
* TODO(user): Consider hacking page, media, etc.. to work.
21
* This would be pretty challenging. IE returns the text for any rule
22
* regardless of whether or not the media is correct or not. Firefox at
23
* least supports CSSRule.type to figure out if it's a media type and then
24
* we could do something interesting, but IE offers no way for us to tell.
25
*/
26
27
goog.provide('goog.cssom');
28
goog.provide('goog.cssom.CssRuleType');
29
30
goog.require('goog.array');
31
goog.require('goog.dom');
32
goog.require('goog.dom.TagName');
33
34
35
/**
36
* Enumeration of {@code CSSRule} types.
37
* @enum {number}
38
*/
39
goog.cssom.CssRuleType = {
40
STYLE: 1,
41
IMPORT: 3,
42
MEDIA: 4,
43
FONT_FACE: 5,
44
PAGE: 6,
45
NAMESPACE: 7
46
};
47
48
49
/**
50
* Recursively gets all CSS as text, optionally starting from a given
51
* CSSStyleSheet.
52
* @param {(CSSStyleSheet|StyleSheetList)=} opt_styleSheet The CSSStyleSheet.
53
* @return {string} css text.
54
*/
55
goog.cssom.getAllCssText = function(opt_styleSheet) {
56
var styleSheet = opt_styleSheet || document.styleSheets;
57
return /** @type {string} */ (goog.cssom.getAllCss_(styleSheet, true));
58
};
59
60
61
/**
62
* Recursively gets all CSSStyleRules, optionally starting from a given
63
* CSSStyleSheet.
64
* Note that this excludes any CSSImportRules, CSSMediaRules, etc..
65
* @param {(CSSStyleSheet|StyleSheetList)=} opt_styleSheet The CSSStyleSheet.
66
* @return {Array<CSSStyleRule>} A list of CSSStyleRules.
67
*/
68
goog.cssom.getAllCssStyleRules = function(opt_styleSheet) {
69
var styleSheet = opt_styleSheet || document.styleSheets;
70
return /** @type {!Array<CSSStyleRule>} */ (
71
goog.cssom.getAllCss_(styleSheet, false));
72
};
73
74
75
/**
76
* Returns the CSSRules from a styleSheet.
77
* Worth noting here is that IE and FF differ in terms of what they will return.
78
* Firefox will return styleSheet.cssRules, which includes ImportRules and
79
* anything which implements the CSSRules interface. IE returns simply a list of
80
* CSSRules.
81
* @param {CSSStyleSheet} styleSheet The CSSStyleSheet.
82
* @throws {Error} If we cannot access the rules on a stylesheet object - this
83
* can happen if a stylesheet object's rules are accessed before the rules
84
* have been downloaded and parsed and are "ready".
85
* @return {CSSRuleList} An array of CSSRules or null.
86
*/
87
goog.cssom.getCssRulesFromStyleSheet = function(styleSheet) {
88
var cssRuleList = null;
89
try {
90
// Select cssRules unless it isn't present. For pre-IE9 IE, use the rules
91
// collection instead.
92
// It's important to be consistent in using only the W3C or IE apis on
93
// IE9+ where both are present to ensure that there is no indexing
94
// mismatches - the collections are subtly different in what the include or
95
// exclude which can lead to one collection being longer than the other
96
// depending on the page's construction.
97
cssRuleList = styleSheet.cssRules /* W3C */ || styleSheet.rules /* IE */;
98
} catch (e) {
99
// This can happen if we try to access the CSSOM before it's "ready".
100
if (e.code == 15) {
101
// Firefox throws an NS_ERROR_DOM_INVALID_ACCESS_ERR error if a stylesheet
102
// is read before it has been fully parsed. Let the caller know which
103
// stylesheet failed.
104
e.styleSheet = styleSheet;
105
throw e;
106
}
107
}
108
return cssRuleList;
109
};
110
111
112
/**
113
* Gets all CSSStyleSheet objects starting from some CSSStyleSheet. Note that we
114
* want to return the sheets in the order of the cascade, therefore if we
115
* encounter an import, we will splice that CSSStyleSheet object in front of
116
* the CSSStyleSheet that contains it in the returned array of CSSStyleSheets.
117
* @param {(CSSStyleSheet|StyleSheetList)=} opt_styleSheet A CSSStyleSheet.
118
* @param {boolean=} opt_includeDisabled If true, includes disabled stylesheets,
119
* defaults to false.
120
* @return {!Array<CSSStyleSheet>} A list of CSSStyleSheet objects.
121
*/
122
goog.cssom.getAllCssStyleSheets = function(
123
opt_styleSheet, opt_includeDisabled) {
124
var styleSheetsOutput = [];
125
var styleSheet = opt_styleSheet || document.styleSheets;
126
var includeDisabled =
127
goog.isDef(opt_includeDisabled) ? opt_includeDisabled : false;
128
129
// Imports need to go first.
130
if (styleSheet.imports && styleSheet.imports.length) {
131
for (var i = 0, n = styleSheet.imports.length; i < n; i++) {
132
goog.array.extend(
133
styleSheetsOutput,
134
goog.cssom.getAllCssStyleSheets(
135
/** @type {CSSStyleSheet} */ (styleSheet.imports[i])));
136
}
137
138
} else if (styleSheet.length) {
139
// In case we get a StyleSheetList object.
140
// http://dev.w3.org/csswg/cssom/#the-stylesheetlist
141
for (var i = 0, n = styleSheet.length; i < n; i++) {
142
goog.array.extend(
143
styleSheetsOutput, goog.cssom.getAllCssStyleSheets(
144
/** @type {!CSSStyleSheet} */ (styleSheet[i])));
145
}
146
} else {
147
// We need to walk through rules in browsers which implement .cssRules
148
// to see if there are styleSheets buried in there.
149
// If we have a CSSStyleSheet within CssRules.
150
var cssRuleList = goog.cssom.getCssRulesFromStyleSheet(
151
/** @type {!CSSStyleSheet} */ (styleSheet));
152
if (cssRuleList && cssRuleList.length) {
153
// Chrome does not evaluate cssRuleList[i] to undefined when i >=n;
154
// so we use a (i < n) check instead of cssRuleList[i] in the loop below
155
// and in other places where we iterate over a rules list.
156
// See issue # 5917 in Chromium.
157
for (var i = 0, n = cssRuleList.length, cssRule; i < n; i++) {
158
cssRule = cssRuleList[i];
159
// There are more stylesheets to get on this object..
160
if (cssRule.styleSheet) {
161
goog.array.extend(
162
styleSheetsOutput,
163
goog.cssom.getAllCssStyleSheets(cssRule.styleSheet));
164
}
165
}
166
}
167
}
168
169
// This is a CSSStyleSheet. (IE uses .rules, W3c and Opera cssRules.)
170
if ((styleSheet.type || styleSheet.rules || styleSheet.cssRules) &&
171
(!styleSheet.disabled || includeDisabled)) {
172
styleSheetsOutput.push(styleSheet);
173
}
174
175
return styleSheetsOutput;
176
};
177
178
179
/**
180
* Gets the cssText from a CSSRule object cross-browserly.
181
* @param {CSSRule} cssRule A CSSRule.
182
* @return {string} cssText The text for the rule, including the selector.
183
*/
184
goog.cssom.getCssTextFromCssRule = function(cssRule) {
185
var cssText = '';
186
187
if (cssRule.cssText) {
188
// W3C.
189
cssText = cssRule.cssText;
190
} else if (cssRule.style && cssRule.style.cssText && cssRule.selectorText) {
191
// IE: The spacing here is intended to make the result consistent with
192
// FF and Webkit.
193
// We also remove the special properties that we may have added in
194
// getAllCssStyleRules since IE includes those in the cssText.
195
var styleCssText =
196
cssRule.style.cssText
197
.replace(/\s*-closure-parent-stylesheet:\s*\[object\];?\s*/gi, '')
198
.replace(/\s*-closure-rule-index:\s*[\d]+;?\s*/gi, '');
199
var thisCssText = cssRule.selectorText + ' { ' + styleCssText + ' }';
200
cssText = thisCssText;
201
}
202
203
return cssText;
204
};
205
206
207
/**
208
* Get the index of the CSSRule in it's CSSStyleSheet.
209
* @param {CSSRule} cssRule A CSSRule.
210
* @param {CSSStyleSheet=} opt_parentStyleSheet A reference to the stylesheet
211
* object this cssRule belongs to.
212
* @throws {Error} When we cannot get the parentStyleSheet.
213
* @return {number} The index of the CSSRule, or -1.
214
*/
215
goog.cssom.getCssRuleIndexInParentStyleSheet = function(
216
cssRule, opt_parentStyleSheet) {
217
// Look for our special style.ruleIndex property from getAllCss.
218
if (cssRule.style && /** @type {!Object} */ (cssRule.style)['-closure-rule-index']) {
219
return (/** @type {!Object} */ (cssRule.style))['-closure-rule-index'];
220
}
221
222
var parentStyleSheet =
223
opt_parentStyleSheet || goog.cssom.getParentStyleSheet(cssRule);
224
225
if (!parentStyleSheet) {
226
// We could call getAllCssStyleRules() here to get our special indexes on
227
// the style object, but that seems like it could be wasteful.
228
throw Error('Cannot find a parentStyleSheet.');
229
}
230
231
var cssRuleList = goog.cssom.getCssRulesFromStyleSheet(parentStyleSheet);
232
if (cssRuleList && cssRuleList.length) {
233
for (var i = 0, n = cssRuleList.length, thisCssRule; i < n; i++) {
234
thisCssRule = cssRuleList[i];
235
if (thisCssRule == cssRule) {
236
return i;
237
}
238
}
239
}
240
return -1;
241
};
242
243
244
/**
245
* We do some trickery in getAllCssStyleRules that hacks this in for IE.
246
* If the cssRule object isn't coming from a result of that function call, this
247
* method will return undefined in IE.
248
* @param {CSSRule} cssRule The CSSRule.
249
* @return {CSSStyleSheet} A styleSheet object.
250
*/
251
goog.cssom.getParentStyleSheet = function(cssRule) {
252
return cssRule.parentStyleSheet ||
253
cssRule.style &&
254
(/** @type {!Object} */ (cssRule.style))['-closure-parent-stylesheet'];
255
};
256
257
258
/**
259
* Replace a cssRule with some cssText for a new rule.
260
* If the cssRule object is not one of objects returned by
261
* getAllCssStyleRules, then you'll need to provide both the styleSheet and
262
* possibly the index, since we can't infer them from the standard cssRule
263
* object in IE. We do some trickery in getAllCssStyleRules to hack this in.
264
* @param {CSSRule} cssRule A CSSRule.
265
* @param {string} cssText The text for the new CSSRule.
266
* @param {CSSStyleSheet=} opt_parentStyleSheet A reference to the stylesheet
267
* object this cssRule belongs to.
268
* @param {number=} opt_index The index of the cssRule in its parentStylesheet.
269
* @throws {Error} If we cannot find a parentStyleSheet.
270
* @throws {Error} If we cannot find a css rule index.
271
*/
272
goog.cssom.replaceCssRule = function(
273
cssRule, cssText, opt_parentStyleSheet, opt_index) {
274
var parentStyleSheet =
275
opt_parentStyleSheet || goog.cssom.getParentStyleSheet(cssRule);
276
if (parentStyleSheet) {
277
var index = Number(opt_index) >= 0 ?
278
Number(opt_index) :
279
goog.cssom.getCssRuleIndexInParentStyleSheet(cssRule, parentStyleSheet);
280
if (index >= 0) {
281
goog.cssom.removeCssRule(parentStyleSheet, index);
282
goog.cssom.addCssRule(parentStyleSheet, cssText, index);
283
} else {
284
throw Error('Cannot proceed without the index of the cssRule.');
285
}
286
} else {
287
throw Error('Cannot proceed without the parentStyleSheet.');
288
}
289
};
290
291
292
/**
293
* Cross browser function to add a CSSRule into a CSSStyleSheet, optionally
294
* at a given index.
295
* @param {CSSStyleSheet} cssStyleSheet The CSSRule's parentStyleSheet.
296
* @param {string} cssText The text for the new CSSRule.
297
* @param {number=} opt_index The index of the cssRule in its parentStylesheet.
298
* @throws {Error} If the css rule text appears to be ill-formatted.
299
* TODO(bowdidge): Inserting at index 0 fails on Firefox 2 and 3 with an
300
* exception warning "Node cannot be inserted at the specified point in
301
* the hierarchy."
302
*/
303
goog.cssom.addCssRule = function(cssStyleSheet, cssText, opt_index) {
304
var index = opt_index;
305
if (index == undefined || index < 0) {
306
// If no index specified, insert at the end of the current list
307
// of rules.
308
var rules = goog.cssom.getCssRulesFromStyleSheet(cssStyleSheet);
309
index = rules.length;
310
}
311
if (cssStyleSheet.insertRule) {
312
// W3C (including IE9+).
313
cssStyleSheet.insertRule(cssText, index);
314
315
} else {
316
// IE, pre 9: We have to parse the cssRule text to get the selector
317
// separated from the style text.
318
// aka Everything that isn't a colon, followed by a colon, then
319
// the rest is the style part.
320
var matches = /^([^\{]+)\{([^\{]+)\}/.exec(cssText);
321
if (matches.length == 3) {
322
var selector = matches[1];
323
var style = matches[2];
324
cssStyleSheet.addRule(selector, style, index);
325
} else {
326
throw Error('Your CSSRule appears to be ill-formatted.');
327
}
328
}
329
};
330
331
332
/**
333
* Cross browser function to remove a CSSRule in a CSSStyleSheet at an index.
334
* @param {CSSStyleSheet} cssStyleSheet The CSSRule's parentStyleSheet.
335
* @param {number} index The CSSRule's index in the parentStyleSheet.
336
*/
337
goog.cssom.removeCssRule = function(cssStyleSheet, index) {
338
if (cssStyleSheet.deleteRule) {
339
// W3C.
340
cssStyleSheet.deleteRule(index);
341
342
} else {
343
// IE.
344
cssStyleSheet.removeRule(index);
345
}
346
};
347
348
349
/**
350
* Appends a DOM node to HEAD containing the css text that's passed in.
351
* @param {string} cssText CSS to add to the end of the document.
352
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper user for
353
* document interactions.
354
* @return {!Element} The newly created STYLE element.
355
*/
356
goog.cssom.addCssText = function(cssText, opt_domHelper) {
357
var domHelper = opt_domHelper || goog.dom.getDomHelper();
358
var document = domHelper.getDocument();
359
var cssNode = domHelper.createElement(goog.dom.TagName.STYLE);
360
cssNode.type = 'text/css';
361
var head = domHelper.getElementsByTagName(goog.dom.TagName.HEAD)[0];
362
head.appendChild(cssNode);
363
if (cssNode.styleSheet) {
364
// IE.
365
cssNode.styleSheet.cssText = cssText;
366
} else {
367
// W3C.
368
var cssTextNode = document.createTextNode(cssText);
369
cssNode.appendChild(cssTextNode);
370
}
371
return cssNode;
372
};
373
374
375
/**
376
* Cross browser method to get the filename from the StyleSheet's href.
377
* Explorer only returns the filename in the href, while other agents return
378
* the full path.
379
* @param {!StyleSheet} styleSheet Any valid StyleSheet object with an href.
380
* @throws {Error} When there's no href property found.
381
* @return {?string} filename The filename, or null if not an external
382
* styleSheet.
383
*/
384
goog.cssom.getFileNameFromStyleSheet = function(styleSheet) {
385
var href = styleSheet.href;
386
387
// Another IE/FF difference. IE returns an empty string, while FF and others
388
// return null for CSSStyleSheets not from an external file.
389
if (!href) {
390
return null;
391
}
392
393
// We need the regexp to ensure we get the filename minus any query params.
394
var matches = /([^\/\?]+)[^\/]*$/.exec(href);
395
var filename = matches[1];
396
return filename;
397
};
398
399
400
/**
401
* Recursively gets all CSS text or rules.
402
* @param {CSSStyleSheet|StyleSheetList} styleSheet The CSSStyleSheet.
403
* @param {boolean} isTextOutput If true, output is cssText, otherwise cssRules.
404
* @return {string|!Array<CSSRule>} cssText or cssRules.
405
* @private
406
*/
407
goog.cssom.getAllCss_ = function(styleSheet, isTextOutput) {
408
var cssOut = [];
409
var styleSheets = goog.cssom.getAllCssStyleSheets(styleSheet);
410
411
for (var i = 0; styleSheet = styleSheets[i]; i++) {
412
var cssRuleList = goog.cssom.getCssRulesFromStyleSheet(styleSheet);
413
414
if (cssRuleList && cssRuleList.length) {
415
var ruleIndex = 0;
416
for (var j = 0, n = cssRuleList.length, cssRule; j < n; j++) {
417
cssRule = cssRuleList[j];
418
// Gets cssText output, ignoring CSSImportRules.
419
if (isTextOutput && !cssRule.href) {
420
var res = goog.cssom.getCssTextFromCssRule(cssRule);
421
cssOut.push(res);
422
423
} else if (!cssRule.href) {
424
// Gets cssRules output, ignoring CSSImportRules.
425
if (cssRule.style) {
426
// This is a fun little hack to get parentStyleSheet into the rule
427
// object for IE since it failed to implement rule.parentStyleSheet.
428
// We can later read this property when doing things like hunting
429
// for indexes in order to delete a given CSSRule.
430
// Unfortunately we have to use the style object to store these
431
// pieces of info since the rule object is read-only.
432
if (!cssRule.parentStyleSheet) {
433
(/** @type {!Object} */ (cssRule.style))[
434
'-closure-parent-stylesheet'] = styleSheet;
435
}
436
437
// This is a hack to help with possible removal of the rule later,
438
// where we just append the rule's index in its parentStyleSheet
439
// onto the style object as a property.
440
// Unfortunately we have to use the style object to store these
441
// pieces of info since the rule object is read-only.
442
(/** @type {!Object} */ (cssRule.style))['-closure-rule-index'] =
443
isTextOutput ? undefined : ruleIndex;
444
}
445
cssOut.push(cssRule);
446
}
447
if (!isTextOutput) {
448
ruleIndex++;
449
}
450
}
451
}
452
}
453
return isTextOutput ? cssOut.join(' ') : cssOut;
454
};
455
456