blob: 2cb369109802beb7f028456ca20fcfde2fe69db6 [file] [log] [blame]
// Copyright 2010 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
goog.provide('goog.cssom.iframe.styleTest');
goog.setTestOnly('goog.cssom.iframe.styleTest');
goog.require('goog.cssom');
goog.require('goog.cssom.iframe.style');
goog.require('goog.dom');
goog.require('goog.dom.DomHelper');
goog.require('goog.testing.jsunit');
goog.require('goog.userAgent');
// unit tests
var propertiesToTest = [
'color',
'font-family',
'font-style',
'font-size',
'font-variant',
'border-top-style',
'border-top-width',
'border-top-color',
'background-color',
'margin-bottom'
];
function crawlDom(startNode, func) {
if (startNode.nodeType != 1) { return; }
func(startNode);
for (var i = 0; i < startNode.childNodes.length; i++) {
crawlDom(startNode.childNodes[i], func);
}
}
function getCurrentCssProperties(node, propList) {
var props = {};
if (node.nodeType != 1) { return; }
for (var i = 0; i < propList.length; i++) {
var prop = propList[i];
if (node.currentStyle) { // IE
var propCamelCase = '';
var propParts = prop.split('-');
for (var j = 0; j < propParts.length; j++) {
propCamelCase += propParts[j].charAt(0).toUpperCase() +
propParts[j].substring(1, propParts[j].length);
}
props[prop] = node.currentStyle[propCamelCase];
} else { // standards-compliant browsers
props[prop] = node.ownerDocument.defaultView.getComputedStyle(
node, '').getPropertyValue(prop);
}
}
return props;
}
function CssPropertyCollector() {
var propsList = [];
this.propsList = propsList;
this.collectProps = function(node) {
var nodeProps = getCurrentCssProperties(node, propertiesToTest);
if (nodeProps) { propsList.push([nodeProps, node]); }
};
}
function recursivelyListCssProperties(el) {
var collector = new CssPropertyCollector();
crawlDom(el, collector.collectProps);
return collector.propsList;
}
function testMatchCssSelector() {
var container = document.createElement('div');
container.className = 'container';
var el = document.createElement('div');
x = el;
el.id = 'mydiv';
el.className = 'colorful foo';
// set some arbirtrary content
el.innerHTML = '<div><ul><li>One</li><li>Two</li></ul></div>';
container.appendChild(el);
document.body.appendChild(container);
var elementAncestry = new goog.cssom.iframe.style.NodeAncestry_(el);
assertEquals(5, elementAncestry.nodes.length);
// list of input/output results. Output is the index of the selector
// that we expect to match - for example, in 'body div div.colorful',
// 'div.colorful' has an index of 2.
var expectedResults = [
['body div', [4, 1]],
['h1', null],
['body div h1', [4, 1]],
['body div.colorful h1', [4, 1]],
['body div div', [4, 2]],
['body div div div', [4, 2]],
['body div div.somethingelse div', [4, 1]],
['body div.somethingelse div', [2, 0]],
['div.container', [3, 0]],
['div.container div', [4, 1]],
['#mydiv', [4, 0]],
['div#mydiv', [4, 0]],
['div.colorful', [4, 0]],
['div#mydiv .colorful', [4, 0]],
['.colorful', [4, 0]],
['body * div', [4, 2]],
['body * *', [4, 2]]
];
for (var i = 0; i < expectedResults.length; i++) {
var input = expectedResults[i][0];
var expectedResult = expectedResults[i][1];
var selector = new goog.cssom.iframe.style.CssSelector_(input);
var result = selector.matchElementAncestry(elementAncestry);
if (expectedResult == null) {
assertEquals('Expected null result', expectedResult, result);
} else {
assertEquals('Expected element index for ' + input,
expectedResult[0],
result.elementIndex);
assertEquals('Expected selector part index for ' + input,
expectedResult[1],
result.selectorPartIndex);
}
}
document.body.removeChild(container);
}
function makeIframeDocument(iframe) {
var doc = goog.dom.getFrameContentDocument(iframe);
doc.open();
doc.write('<html><head>');
doc.write('<style>html,body { background-color: transparent; }</style>');
doc.write('</head><body></body></html>');
doc.close();
return doc;
}
function testCopyCss() {
for (var i = 1; i <= 4; i++) {
var sourceElement = document.getElementById('source' + i);
var newFrame = document.createElement('iframe');
newFrame.allowTransparency = true;
sourceElement.parentNode.insertBefore(newFrame,
sourceElement.nextSibling);
var doc = makeIframeDocument(newFrame);
goog.cssom.addCssText(
goog.cssom.iframe.style.getElementContext(sourceElement),
new goog.dom.DomHelper(doc));
doc.body.innerHTML = sourceElement.innerHTML;
var oldProps = recursivelyListCssProperties(sourceElement);
var newProps = recursivelyListCssProperties(doc.body);
assertEquals(oldProps.length, newProps.length);
for (var j = 0; j < oldProps.length; j++) {
for (var k = 0; k < propertiesToTest.length; k++) {
assertEquals('testing property ' + propertiesToTest[k],
oldProps[j][0][propertiesToTest[k]],
newProps[j][0][propertiesToTest[k]]);
}
}
}
}
function normalizeCssText(cssText) {
// Normalize cssText for testing purposes.
return cssText.replace(/\s/g, '').toLowerCase();
}
function testAImportantInFF2() {
var testDiv = document.getElementById('source1');
var cssText = normalizeCssText(
goog.cssom.iframe.style.getElementContext(testDiv));
var color = standardizeCSSValue('color', 'red');
var NORMAL_RULE = 'a{color:' + color;
var FF_2_RULE = 'a{color:' + color + '!important';
if (goog.userAgent.GECKO && !goog.userAgent.isVersionOrHigher('1.9a')) {
assertContains(FF_2_RULE, cssText);
} else {
assertContains(NORMAL_RULE, cssText);
assertNotContains(FF_2_RULE, cssText);
}
}
function testCopyBackgroundContext() {
var testDiv = document.getElementById('backgroundTest');
var cssText = goog.cssom.iframe.style.getElementContext(testDiv,
null,
true);
var iframe = document.createElement('iframe');
var ancestor = document.getElementById('backgroundTest-ancestor-1');
ancestor.parentNode.insertBefore(iframe, ancestor.nextSibling);
iframe.style.width = '100%';
iframe.style.height = '100px';
iframe.style.borderWidth = '0px';
var doc = makeIframeDocument(iframe);
goog.cssom.addCssText(cssText, new goog.dom.DomHelper(doc));
doc.body.innerHTML = testDiv.innerHTML;
var normalizedCssText = normalizeCssText(cssText);
assertTrue(
'Background color should be copied from parent element',
/body{[^{]*background-color:(?:rgb\(128,0,128\)|#800080)/.test(
normalizedCssText));
assertTrue(
'Background image should be copied from ancestor element',
/body{[^{]*background-image:url\(/.test(normalizedCssText));
// Background-position can't be calculated in FF2, due to this bug:
// http://bugzilla.mozilla.org/show_bug.cgi?id=316981
if (!(goog.userAgent.GECKO && !goog.userAgent.isVersionOrHigher('1.9'))) {
// Expected x position is:
// originalBackgroundPositionX - elementOffsetLeft
// 40px - (1px + 8px) == 31px
// Expected y position is:
// originalBackgroundPositionY - elementOffsetLeft
// 70px - (1px + 10px + 5px) == 54px;
assertTrue('Background image position should be adjusted correctly',
/body{[^{]*background-position:31px54px/.test(normalizedCssText));
}
}
function testCopyBackgroundContextFromIframe() {
var testDiv = document.getElementById('backgroundTest');
var iframe = document.createElement('iframe');
iframe.allowTransparency = true;
iframe.style.position = 'absolute';
iframe.style.top = '5px';
iframe.style.left = '5px';
iframe.style.borderWidth = '2px';
iframe.style.borderStyle = 'solid';
testDiv.appendChild(iframe);
var doc = makeIframeDocument(iframe);
doc.body.backgroundColor = 'transparent';
doc.body.style.margin = '0';
doc.body.style.padding = '0';
doc.body.innerHTML = '<p style="margin: 0">I am transparent!</p>';
var normalizedCssText = normalizeCssText(
goog.cssom.iframe.style.getElementContext(
doc.body.firstChild, null, true));
// Background properties should get copied through from the parent
// document since the iframe is transparent
assertTrue(
'Background color should be copied from parent element',
/body{[^{]*background-color:(?:rgb\(128,0,128\)|#800080)/.test(
normalizedCssText));
assertTrue(
'Background image should be copied from ancestor element',
/body{[^{]*background-image:url\(/.test(normalizedCssText));
// Background-position can't be calculated in FF2, due to this bug:
// http://bugzilla.mozilla.org/show_bug.cgi?id=316981
if (!(goog.userAgent.GECKO && !goog.userAgent.isVersionOrHigher('1.9'))) {
// Image offset should have been calculated to be the same as the
// above example, but adding iframe offset and borderWidth.
// Expected x position is:
// originalBackgroundPositionX - elementOffsetLeft
// 40px - (1px + 8px + 5px + 2px) == 24px
// Expected y position is:
// originalBackgroundPositionY - elementOffsetLeft
// 70px - (1px + 10px + 5px + 5px + 2px) == 47px;
assertTrue('Background image position should be adjusted correctly',
!!/body{[^{]*background-position:24px47px/.exec(
normalizedCssText));
}
iframe.parentNode.removeChild(iframe);
}
function testCopyFontFaceRules() {
var isFontFaceCssomSupported =
goog.userAgent.WEBKIT ||
goog.userAgent.OPERA ||
(goog.userAgent.GECKO && goog.userAgent.isVersionOrHigher('1.9.1'));
// We cannot use goog.testing.ExpectedFailures since it dynamically
// brings in CSS which causes the background context tests to fail
// in IE6.
if (isFontFaceCssomSupported) {
var cssText = goog.cssom.iframe.style.getElementContext(
document.getElementById('cavalier'));
assertTrue('The font face rule should have been copied correctly',
/@font-face/.test(cssText));
}
}