style_test.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. // Copyright 2010 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. goog.provide('goog.cssom.iframe.styleTest');
  15. goog.setTestOnly('goog.cssom.iframe.styleTest');
  16. goog.require('goog.cssom');
  17. goog.require('goog.cssom.iframe.style');
  18. goog.require('goog.dom');
  19. goog.require('goog.dom.DomHelper');
  20. goog.require('goog.dom.TagName');
  21. goog.require('goog.testing.jsunit');
  22. goog.require('goog.userAgent');
  23. // unit tests
  24. var propertiesToTest = [
  25. 'color', 'font-family', 'font-style', 'font-size', 'font-variant',
  26. 'border-top-style', 'border-top-width', 'border-top-color',
  27. 'background-color', 'margin-bottom'
  28. ];
  29. function crawlDom(startNode, func) {
  30. if (startNode.nodeType != 1) {
  31. return;
  32. }
  33. func(startNode);
  34. for (var i = 0; i < startNode.childNodes.length; i++) {
  35. crawlDom(startNode.childNodes[i], func);
  36. }
  37. }
  38. function getCurrentCssProperties(node, propList) {
  39. var props = {};
  40. if (node.nodeType != 1) {
  41. return;
  42. }
  43. for (var i = 0; i < propList.length; i++) {
  44. var prop = propList[i];
  45. if (node.currentStyle) { // IE
  46. var propCamelCase = '';
  47. var propParts = prop.split('-');
  48. for (var j = 0; j < propParts.length; j++) {
  49. propCamelCase += propParts[j].charAt(0).toUpperCase() +
  50. propParts[j].substring(1, propParts[j].length);
  51. }
  52. props[prop] = node.currentStyle[propCamelCase];
  53. } else { // standards-compliant browsers
  54. props[prop] = node.ownerDocument.defaultView.getComputedStyle(node, '')
  55. .getPropertyValue(prop);
  56. }
  57. }
  58. return props;
  59. }
  60. function CssPropertyCollector() {
  61. var propsList = [];
  62. this.propsList = propsList;
  63. this.collectProps = function(node) {
  64. var nodeProps = getCurrentCssProperties(node, propertiesToTest);
  65. if (nodeProps) {
  66. propsList.push([nodeProps, node]);
  67. }
  68. };
  69. }
  70. function recursivelyListCssProperties(el) {
  71. var collector = new CssPropertyCollector();
  72. crawlDom(el, collector.collectProps);
  73. return collector.propsList;
  74. }
  75. function testMatchCssSelector() {
  76. var container = goog.dom.createElement(goog.dom.TagName.DIV);
  77. container.className = 'container';
  78. var el = goog.dom.createElement(goog.dom.TagName.DIV);
  79. x = el;
  80. el.id = 'mydiv';
  81. el.className = 'colorful foo';
  82. // set some arbirtrary content
  83. el.innerHTML = '<div><ul><li>One</li><li>Two</li></ul></div>';
  84. container.appendChild(el);
  85. document.body.appendChild(container);
  86. var elementAncestry = new goog.cssom.iframe.style.NodeAncestry_(el);
  87. assertEquals(5, elementAncestry.nodes.length);
  88. // list of input/output results. Output is the index of the selector
  89. // that we expect to match - for example, in 'body div div.colorful',
  90. // 'div.colorful' has an index of 2.
  91. var expectedResults = [
  92. ['body div', [4, 1]], ['h1', null], ['body div h1', [4, 1]],
  93. ['body div.colorful h1', [4, 1]], ['body div div', [4, 2]],
  94. ['body div div div', [4, 2]], ['body div div.somethingelse div', [4, 1]],
  95. ['body div.somethingelse div', [2, 0]], ['div.container', [3, 0]],
  96. ['div.container div', [4, 1]], ['#mydiv', [4, 0]], ['div#mydiv', [4, 0]],
  97. ['div.colorful', [4, 0]], ['div#mydiv .colorful', [4, 0]],
  98. ['.colorful', [4, 0]], ['body * div', [4, 2]], ['body * *', [4, 2]]
  99. ];
  100. for (var i = 0; i < expectedResults.length; i++) {
  101. var input = expectedResults[i][0];
  102. var expectedResult = expectedResults[i][1];
  103. var selector = new goog.cssom.iframe.style.CssSelector_(input);
  104. var result = selector.matchElementAncestry(elementAncestry);
  105. if (expectedResult == null) {
  106. assertEquals('Expected null result', expectedResult, result);
  107. } else {
  108. assertEquals(
  109. 'Expected element index for ' + input, expectedResult[0],
  110. result.elementIndex);
  111. assertEquals(
  112. 'Expected selector part index for ' + input, expectedResult[1],
  113. result.selectorPartIndex);
  114. }
  115. }
  116. document.body.removeChild(container);
  117. }
  118. function makeIframeDocument(iframe) {
  119. var doc = goog.dom.getFrameContentDocument(iframe);
  120. doc.open();
  121. doc.write('<html><head>');
  122. doc.write('<style>html,body { background-color: transparent; }</style>');
  123. doc.write('</head><body></body></html>');
  124. doc.close();
  125. return doc;
  126. }
  127. function testCopyCss() {
  128. for (var i = 1; i <= 4; i++) {
  129. var sourceElement = document.getElementById('source' + i);
  130. var newFrame = goog.dom.createElement(goog.dom.TagName.IFRAME);
  131. newFrame.allowTransparency = true;
  132. sourceElement.parentNode.insertBefore(newFrame, sourceElement.nextSibling);
  133. var doc = makeIframeDocument(newFrame);
  134. goog.cssom.addCssText(
  135. goog.cssom.iframe.style.getElementContext(sourceElement),
  136. new goog.dom.DomHelper(doc));
  137. doc.body.innerHTML = sourceElement.innerHTML;
  138. var oldProps = recursivelyListCssProperties(sourceElement);
  139. var newProps = recursivelyListCssProperties(doc.body);
  140. assertEquals(oldProps.length, newProps.length);
  141. for (var j = 0; j < oldProps.length; j++) {
  142. for (var k = 0; k < propertiesToTest.length; k++) {
  143. assertEquals(
  144. 'testing property ' + propertiesToTest[k],
  145. oldProps[j][0][propertiesToTest[k]],
  146. newProps[j][0][propertiesToTest[k]]);
  147. }
  148. }
  149. }
  150. }
  151. function normalizeCssText(cssText) {
  152. // Normalize cssText for testing purposes.
  153. return cssText.replace(/\s/g, '').toLowerCase();
  154. }
  155. function testAImportantInFF2() {
  156. var testDiv = document.getElementById('source1');
  157. var cssText =
  158. normalizeCssText(goog.cssom.iframe.style.getElementContext(testDiv));
  159. var color = standardizeCSSValue('color', 'red');
  160. var NORMAL_RULE = 'a{color:' + color;
  161. var FF_2_RULE = 'a{color:' + color + '!important';
  162. if (goog.userAgent.GECKO && !goog.userAgent.isVersionOrHigher('1.9a')) {
  163. assertContains(FF_2_RULE, cssText);
  164. } else {
  165. assertContains(NORMAL_RULE, cssText);
  166. assertNotContains(FF_2_RULE, cssText);
  167. }
  168. }
  169. function testCopyBackgroundContext() {
  170. var testDiv = document.getElementById('backgroundTest');
  171. var cssText = goog.cssom.iframe.style.getElementContext(testDiv, null, true);
  172. var iframe = goog.dom.createElement(goog.dom.TagName.IFRAME);
  173. var ancestor = document.getElementById('backgroundTest-ancestor-1');
  174. ancestor.parentNode.insertBefore(iframe, ancestor.nextSibling);
  175. iframe.style.width = '100%';
  176. iframe.style.height = '100px';
  177. iframe.style.borderWidth = '0px';
  178. var doc = makeIframeDocument(iframe);
  179. goog.cssom.addCssText(cssText, new goog.dom.DomHelper(doc));
  180. doc.body.innerHTML = testDiv.innerHTML;
  181. var normalizedCssText = normalizeCssText(cssText);
  182. assertTrue(
  183. 'Background color should be copied from parent element',
  184. /body{[^{]*background-color:(?:rgb\(128,0,128\)|#800080)/.test(
  185. normalizedCssText));
  186. assertTrue(
  187. 'Background image should be copied from ancestor element',
  188. /body{[^{]*background-image:url\(/.test(normalizedCssText));
  189. // Background-position can't be calculated in FF2, due to this bug:
  190. // http://bugzilla.mozilla.org/show_bug.cgi?id=316981
  191. if (!(goog.userAgent.GECKO && !goog.userAgent.isVersionOrHigher('1.9'))) {
  192. // Expected x position is:
  193. // originalBackgroundPositionX - elementOffsetLeft
  194. // 40px - (1px + 8px) == 31px
  195. // Expected y position is:
  196. // originalBackgroundPositionY - elementOffsetLeft
  197. // 70px - (1px + 10px + 5px) == 54px;
  198. assertTrue(
  199. 'Background image position should be adjusted correctly',
  200. /body{[^{]*background-position:31px54px/.test(normalizedCssText));
  201. }
  202. }
  203. function testCopyBackgroundContextFromIframe() {
  204. var testDiv = document.getElementById('backgroundTest');
  205. var iframe = goog.dom.createElement(goog.dom.TagName.IFRAME);
  206. iframe.allowTransparency = true;
  207. iframe.style.position = 'absolute';
  208. iframe.style.top = '5px';
  209. iframe.style.left = '5px';
  210. iframe.style.borderWidth = '2px';
  211. iframe.style.borderStyle = 'solid';
  212. testDiv.appendChild(iframe);
  213. var doc = makeIframeDocument(iframe);
  214. doc.body.backgroundColor = 'transparent';
  215. doc.body.style.margin = '0';
  216. doc.body.style.padding = '0';
  217. doc.body.innerHTML = '<p style="margin: 0">I am transparent!</p>';
  218. var normalizedCssText = normalizeCssText(
  219. goog.cssom.iframe.style.getElementContext(
  220. doc.body.firstChild, null, true));
  221. // Background properties should get copied through from the parent
  222. // document since the iframe is transparent
  223. assertTrue(
  224. 'Background color should be copied from parent element',
  225. /body{[^{]*background-color:(?:rgb\(128,0,128\)|#800080)/.test(
  226. normalizedCssText));
  227. assertTrue(
  228. 'Background image should be copied from ancestor element',
  229. /body{[^{]*background-image:url\(/.test(normalizedCssText));
  230. // Background-position can't be calculated in FF2, due to this bug:
  231. // http://bugzilla.mozilla.org/show_bug.cgi?id=316981
  232. if (!(goog.userAgent.GECKO && !goog.userAgent.isVersionOrHigher('1.9'))) {
  233. // Image offset should have been calculated to be the same as the
  234. // above example, but adding iframe offset and borderWidth.
  235. // Expected x position is:
  236. // originalBackgroundPositionX - elementOffsetLeft
  237. // 40px - (1px + 8px + 5px + 2px) == 24px
  238. // Expected y position is:
  239. // originalBackgroundPositionY - elementOffsetLeft
  240. // 70px - (1px + 10px + 5px + 5px + 2px) == 47px;
  241. assertTrue(
  242. 'Background image position should be adjusted correctly',
  243. !!/body{[^{]*background-position:24px47px/.exec(normalizedCssText));
  244. }
  245. iframe.parentNode.removeChild(iframe);
  246. }
  247. function testCopyFontFaceRules() {
  248. var isFontFaceCssomSupported = goog.userAgent.WEBKIT ||
  249. goog.userAgent.OPERA ||
  250. (goog.userAgent.GECKO && goog.userAgent.isVersionOrHigher('1.9.1'));
  251. // We cannot use goog.testing.ExpectedFailures since it dynamically
  252. // brings in CSS which causes the background context tests to fail
  253. // in IE6.
  254. if (isFontFaceCssomSupported) {
  255. var cssText = goog.cssom.iframe.style.getElementContext(
  256. document.getElementById('cavalier'));
  257. assertTrue(
  258. 'The font face rule should have been copied correctly',
  259. /@font-face/.test(cssText));
  260. }
  261. }