0001: /*
0002:
0003: ============================================================================
0004: The Apache Software License, Version 1.1
0005: ============================================================================
0006:
0007: Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
0008:
0009: Redistribution and use in source and binary forms, with or without modifica-
0010: tion, are permitted provided that the following conditions are met:
0011:
0012: 1. Redistributions of source code must retain the above copyright notice,
0013: this list of conditions and the following disclaimer.
0014:
0015: 2. Redistributions in binary form must reproduce the above copyright notice,
0016: this list of conditions and the following disclaimer in the documentation
0017: and/or other materials provided with the distribution.
0018:
0019: 3. The end-user documentation included with the redistribution, if any, must
0020: include the following acknowledgment: "This product includes software
0021: developed by the Apache Software Foundation (http://www.apache.org/)."
0022: Alternately, this acknowledgment may appear in the software itself, if
0023: and wherever such third-party acknowledgments normally appear.
0024:
0025: 4. The names "Batik" and "Apache Software Foundation" must not be
0026: used to endorse or promote products derived from this software without
0027: prior written permission. For written permission, please contact
0028: apache@apache.org.
0029:
0030: 5. Products derived from this software may not be called "Apache", nor may
0031: "Apache" appear in their name, without prior written permission of the
0032: Apache Software Foundation.
0033:
0034: THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
0035: INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
0036: FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
0037: APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
0038: INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
0039: DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
0040: OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
0041: ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0042: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
0043: THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0044:
0045: This software consists of voluntary contributions made by many individuals
0046: on behalf of the Apache Software Foundation. For more information on the
0047: Apache Software Foundation, please see <http://www.apache.org/>.
0048:
0049: */
0050:
0051: package org.apache.batik.css.engine;
0052:
0053: import java.io.IOException;
0054: import java.io.StringReader; // <nb>
0055: import java.lang.ref.WeakReference; // </nb>
0056: import java.net.MalformedURLException;
0057: import java.net.URL;
0058: import java.util.ArrayList;
0059: import java.util.Collections;
0060: import java.util.HashSet;
0061: import java.util.LinkedList;
0062: import java.util.List;
0063: import java.util.Set;
0064:
0065: import org.apache.batik.css.engine.sac.CSSConditionFactory;
0066: import org.apache.batik.css.engine.sac.CSSSelectorFactory;
0067: import org.apache.batik.css.engine.sac.ExtendedSelector;
0068: import org.apache.batik.css.engine.value.ComputedValue;
0069: import org.apache.batik.css.engine.value.InheritValue;
0070: import org.apache.batik.css.engine.value.ShorthandManager;
0071: import org.apache.batik.css.engine.value.Value;
0072: import org.apache.batik.css.engine.value.ValueManager;
0073: import org.apache.batik.css.parser.ExtendedParser;
0074: import org.apache.batik.util.CSSConstants;
0075: import org.apache.batik.util.ParsedURL;
0076: import org.w3c.css.sac.CSSException;
0077: import org.w3c.css.sac.DocumentHandler;
0078: import org.w3c.css.sac.InputSource;
0079: import org.w3c.css.sac.LexicalUnit;
0080: import org.w3c.css.sac.SACMediaList;
0081: import org.w3c.css.sac.SelectorList;
0082: import org.w3c.dom.DOMException;
0083: import org.w3c.dom.Document;
0084: import org.w3c.dom.Element;
0085: import org.w3c.dom.NamedNodeMap;
0086: import org.w3c.dom.Node;
0087: import org.w3c.dom.events.Event;
0088: import org.w3c.dom.events.EventListener;
0089: import org.w3c.dom.events.EventTarget;
0090: import org.w3c.dom.events.MutationEvent; // <rave>
0091: // BEGIN RAVE MODIFICATIONS
0092: import java.util.Collection;
0093: import java.util.Comparator;
0094: import java.util.Iterator;
0095: import java.util.Map;
0096: import java.util.TreeSet;
0097: import org.apache.batik.css.engine.value.AbstractValue;
0098: import org.apache.batik.css.parser.AbstractAttributeCondition;
0099: import org.apache.batik.css.engine.sac.CSSClassCondition;
0100: import org.apache.batik.css.engine.sac.CSSConditionalSelector;
0101:
0102: // END RAVE MODIFICATIONS
0103: // </rave>
0104:
0105: /**
0106: * This is the base class for all the CSS engines.
0107: *
0108: * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
0109: * @version $Id$
0110: * @author Tor Norbye (see // BEGIN RAVE MODIFICATIONS markers)
0111: */
0112: public abstract class CSSEngine {
0113:
0114: // <rave>
0115: // BEGIN RAVE MODIFICATIONS
0116: /** Rule filtering will attempt to quickly filter out rules that don't need to be considered when adding matching
0117: * rules for an element - that's currently the slowest operation in the designer.
0118: */
0119: //public static boolean RULE_FILTERING = true;
0120: public static final boolean RULE_FILTERING = System
0121: .getProperty("rave.nocssfiltering") == null;
0122: public static final boolean DEBUG_FILTERING = false;
0123: // END RAVE MODIFICATIONS
0124: // </rave>
0125: /**
0126: * List of StyleMap objects, one for each @font-face rule
0127: * encountered by this CSSEngine.
0128: */
0129: protected List fontFaces = new LinkedList();
0130:
0131: /**
0132: * Get's the StyleMaps generated by @font-face rules
0133: * encountered by this CSSEngine thus far.
0134: */
0135: public List getFontFaces() {
0136: return fontFaces;
0137: }
0138:
0139: CSSEngineUserAgent userAgent = null;
0140:
0141: /**
0142: * Returns the next stylable parent of the given element.
0143: */
0144: public static CSSStylableElement getParentCSSStylableElement(
0145: Element elt) {
0146: // <rave>
0147: // BEGIN RAVE MODIFICATIONS
0148: if (elt instanceof StyleElementLink
0149: && ((StyleElementLink) elt).getStyleParent() != null) {
0150: return ((StyleElementLink) elt).getStyleParent();
0151: }
0152: // END RAVE MODIFICATIONS
0153: // </rave>
0154: Element e = getParentElement(elt);
0155: while (e != null) {
0156: if (e instanceof CSSStylableElement) {
0157: return (CSSStylableElement) e;
0158: }
0159: e = getParentElement(e);
0160: }
0161: return null;
0162: }
0163:
0164: // <rave>
0165: // BEGIN RAVE MODIFICATIONS
0166: public interface StyleElementLink {
0167: /** Return a parent to use for style purposes, if the regular
0168: * parent is null. This is used in rave to connect markup in
0169: * DocumentFragments to the "master" document where the source
0170: * jsf element is located. Only the topmost element in the
0171: * rendered jsf document fragment will return non-null.
0172: */
0173: CSSStylableElement getStyleParent();
0174:
0175: /** Set a parent to use for style purposes. See getStyleParent. */
0176: void setStyleParent(CSSStylableElement parent);
0177: }
0178:
0179: // END RAVE MODIFICATIONS
0180: // </rave>
0181: /**
0182: * Returns the next parent element of the given element, from the
0183: * CSS point of view.
0184: */
0185: public static Element getParentElement(Element elt) {
0186: Node n = elt.getParentNode();
0187: while (n != null) {
0188: n = getLogicalParentNode(n);
0189: if (n.getNodeType() == Node.ELEMENT_NODE) {
0190: return (Element) n;
0191: }
0192: n = n.getParentNode();
0193: }
0194: return null;
0195: }
0196:
0197: /**
0198: * Returns the logical parent of a node, given its physical parent.
0199: */
0200: public static Node getLogicalParentNode(Node parent) {
0201: Node node = parent;
0202: if (node != null) {
0203: if (node instanceof CSSImportedElementRoot) {
0204: return ((CSSImportedElementRoot) node)
0205: .getCSSParentElement();
0206: } else {
0207: return node;
0208: }
0209: }
0210: return null;
0211: }
0212:
0213: /**
0214: * Returns the imported child of the given node, if any.
0215: */
0216: public static CSSImportedElementRoot getImportedChild(Node node) {
0217: if (node instanceof CSSImportNode) {
0218: CSSImportNode inode = (CSSImportNode) node;
0219: CSSImportedElementRoot r = inode
0220: .getCSSImportedElementRoot();
0221: return r;
0222: }
0223: return null;
0224: }
0225:
0226: /**
0227: * The CSS context.
0228: */
0229: protected CSSContext cssContext;
0230:
0231: /**
0232: * The associated document.
0233: */
0234: protected Document document;
0235:
0236: /**
0237: * The document URI.
0238: */
0239: protected URL documentURI;
0240:
0241: /**
0242: * The property/int mappings.
0243: */
0244: protected StringIntMap indexes;
0245:
0246: /**
0247: * The shorthand-property/int mappings.
0248: */
0249: protected StringIntMap shorthandIndexes;
0250:
0251: /**
0252: * The value managers.
0253: */
0254: protected ValueManager[] valueManagers;
0255:
0256: /**
0257: * The shorthand managers.
0258: */
0259: protected ShorthandManager[] shorthandManagers;
0260:
0261: /**
0262: * The CSS parser.
0263: */
0264: protected ExtendedParser parser;
0265:
0266: /**
0267: * The pseudo-element names.
0268: */
0269: protected String[] pseudoElementNames;
0270:
0271: /**
0272: * The font-size property index.
0273: */
0274: protected int fontSizeIndex = -1;
0275:
0276: /**
0277: * The line-height property index.
0278: */
0279: protected int lineHeightIndex = -1;
0280:
0281: /**
0282: * The color property index.
0283: */
0284: protected int colorIndex = -1;
0285:
0286: /**
0287: * The user-agent style-sheet.
0288: */
0289: protected StyleSheet userAgentStyleSheet;
0290:
0291: /**
0292: * The user style-sheet.
0293: */
0294: protected StyleSheet userStyleSheet;
0295:
0296: /**
0297: * The media to use to cascade properties.
0298: */
0299: protected SACMediaList media;
0300:
0301: /**
0302: * The DOM nodes which contains StyleSheets.
0303: */
0304: // <nb> #125149 Memory leak, the style sheet nodes need to be held weakly.
0305: // protected List styleSheetNodes;
0306: // ===
0307: protected List<WeakReference<CSSStyleSheetNode>> styleSheetNodes;
0308: // </nb>
0309:
0310: /**
0311: * The style attribute namespace URI.
0312: */
0313: protected String styleNamespaceURI;
0314:
0315: /**
0316: * The style attribute local name.
0317: */
0318: protected String styleLocalName;
0319:
0320: /**
0321: * The class attribute namespace URI.
0322: */
0323: protected String classNamespaceURI;
0324:
0325: /**
0326: * The class attribute local name.
0327: */
0328: protected String classLocalName;
0329:
0330: /**
0331: * The non CSS presentational hints.
0332: */
0333: protected Set nonCSSPresentationalHints;
0334:
0335: /**
0336: * The non CSS presentational hints namespace URI.
0337: */
0338: protected String nonCSSPresentationalHintsNamespaceURI;
0339:
0340: /**
0341: * The style declaration document handler.
0342: */
0343: protected StyleDeclarationDocumentHandler styleDeclarationDocumentHandler = new StyleDeclarationDocumentHandler();
0344:
0345: /**
0346: * The style declaration update handler.
0347: */
0348: protected StyleDeclarationUpdateHandler styleDeclarationUpdateHandler;
0349:
0350: /**
0351: * The style sheet document handler.
0352: */
0353: protected StyleSheetDocumentHandler styleSheetDocumentHandler = new StyleSheetDocumentHandler();
0354:
0355: /**
0356: * The style declaration document handler used to build a
0357: * StyleDeclaration object.
0358: */
0359: protected StyleDeclarationBuilder styleDeclarationBuilder = new StyleDeclarationBuilder();
0360:
0361: /**
0362: * The current element.
0363: */
0364: protected CSSStylableElement element;
0365:
0366: /**
0367: * The current base URI.
0368: */
0369: protected URL cssBaseURI;
0370:
0371: /**
0372: * The alternate stylesheet title.
0373: */
0374: protected String alternateStyleSheet;
0375:
0376: /**
0377: * The DOMAttrModified event listener.
0378: */
0379: protected EventListener domAttrModifiedListener;
0380:
0381: /**
0382: * The DOMNodeInserted event listener.
0383: */
0384: protected EventListener domNodeInsertedListener;
0385:
0386: /**
0387: * The DOMNodeRemoved event listener.
0388: */
0389: protected EventListener domNodeRemovedListener;
0390:
0391: /**
0392: * The DOMSubtreeModified event listener.
0393: */
0394: protected EventListener domSubtreeModifiedListener;
0395:
0396: /**
0397: * The DOMCharacterDataModified event listener.
0398: */
0399: protected EventListener domCharacterDataModifiedListener;
0400:
0401: /**
0402: * Whether a style sheet as been removed from the document.
0403: */
0404: protected boolean styleSheetRemoved;
0405:
0406: /**
0407: * The right sibling of the last removed node.
0408: */
0409: protected Node removedStylableElementSibling;
0410:
0411: /**
0412: * The listeners.
0413: */
0414: protected List listeners = Collections
0415: .synchronizedList(new LinkedList());
0416:
0417: /**
0418: * The attributes found in stylesheets selectors.
0419: */
0420: protected Set selectorAttributes;
0421:
0422: /**
0423: * Used to fire a change event for all the properties.
0424: */
0425: protected final int[] ALL_PROPERTIES;
0426:
0427: /**
0428: * The CSS condition factory.
0429: */
0430: protected CSSConditionFactory cssConditionFactory;
0431:
0432: /**
0433: * Creates a new CSSEngine.
0434: * @param doc The associated document.
0435: * @param uri The document URI.
0436: * @param p The CSS parser.
0437: * @param vm The property value managers.
0438: * @param sm The shorthand properties managers.
0439: * @param pe The pseudo-element names supported by the associated
0440: * XML dialect. Must be null if no support for pseudo-
0441: * elements is required.
0442: * @param sns The namespace URI of the style attribute.
0443: * @param sln The local name of the style attribute.
0444: * @param cns The namespace URI of the class attribute.
0445: * @param cln The local name of the class attribute.
0446: * @param hints Whether the CSS engine should support non CSS
0447: * presentational hints.
0448: * @param hintsNS The hints namespace URI.
0449: * @param ctx The CSS context.
0450: */
0451: protected CSSEngine(Document doc, URL uri, ExtendedParser p,
0452: ValueManager[] vm, ShorthandManager[] sm, String[] pe,
0453: String sns, String sln, String cns, String cln,
0454: boolean hints, String hintsNS, CSSContext ctx) {
0455: document = doc;
0456: documentURI = uri;
0457: parser = p;
0458: pseudoElementNames = pe;
0459: styleNamespaceURI = sns;
0460: styleLocalName = sln;
0461: classNamespaceURI = cns;
0462: classLocalName = cln;
0463: cssContext = ctx;
0464:
0465: cssConditionFactory = new CSSConditionFactory(cns, cln, null,
0466: "id");
0467:
0468: // <rave>
0469: // BEGIN RAVE MODIFICATIONS
0470: // int len = vm.length;
0471: // indexes = new StringIntMap(len);
0472: // valueManagers = vm;
0473: //
0474: // for (int i = len - 1; i >= 0; --i) {
0475: // String pn = vm[i].getPropertyName();
0476: // indexes.put(pn, i);
0477: // if (fontSizeIndex == -1 &&
0478: // pn.equals(CSSConstants.CSS_FONT_SIZE_PROPERTY)) {
0479: // fontSizeIndex = i;
0480: // }
0481: // if (lineHeightIndex == -1 &&
0482: // pn.equals(CSSConstants.CSS_LINE_HEIGHT_PROPERTY)) {
0483: // lineHeightIndex = i;
0484: // }
0485: // if (colorIndex == -1 &&
0486: // pn.equals(CSSConstants.CSS_COLOR_PROPERTY)) {
0487: // colorIndex = i;
0488: // }
0489: // }
0490: //
0491: // len = sm.length;
0492: // shorthandIndexes = new StringIntMap(len);
0493: // shorthandManagers = sm;
0494: // for (int i = len - 1; i >= 0; --i) {
0495: // shorthandIndexes.put(sm[i].getPropertyName(), i);
0496: // }
0497: // Comment this stuff out, and do it in XhtmlCssEngine where we
0498: // can do a more efficient job (we know the size property indexes
0499: // directly, and more importantly, since it knows that it's sharing
0500: // the value manager and shorthand manager indices between the
0501: // engines, it might as well share the index maps as well!)
0502: // We have to do this to compensate though:
0503: valueManagers = vm;
0504: shorthandManagers = sm;
0505: // END RAVE MODIFICATIONS
0506: // </rave>
0507:
0508: // <rave>
0509: // BEGIN RAVE MODIFICATIONS
0510: // This support was useless, it was simply aliasing CSS names
0511: // to properties! But usually it's not a straight map!
0512: // For example, the HTML attribute "background" maps to the
0513: // CSS property "background-image" ! And the attribute "bgcolor"
0514: // maps to the CSS property "background-color" !
0515: // So instead we'll handle this in subclasses where specific
0516: // logic can be written (the attribute value often has to be
0517: // special handled too so I can't just provide a map to CSSEngine;
0518: // e.g. for "background" for example I need to change the string
0519: // value into a URI.)
0520: // if (hints) {
0521: // len = vm.length;
0522: // nonCSSPresentationalHints = new HashSet();
0523: // nonCSSPresentationalHintsNamespaceURI = hintsNS;
0524: // for (int i = len - 1; i >= 0; --i) {
0525: // String pn = vm[i].getPropertyName();
0526: // nonCSSPresentationalHints.add(pn);
0527: // }
0528: // }
0529: // END RAVE MODIFICATIONS
0530: // </rave>
0531:
0532: if (cssContext.isDynamic() && (document instanceof EventTarget)) {
0533: // Attach the mutation events listeners.
0534: EventTarget et = (EventTarget) document;
0535: domAttrModifiedListener = new DOMAttrModifiedListener();
0536: et.addEventListener("DOMAttrModified",
0537: domAttrModifiedListener, false);
0538: domNodeInsertedListener = new DOMNodeInsertedListener();
0539: et.addEventListener("DOMNodeInserted",
0540: domNodeInsertedListener, false);
0541: domNodeRemovedListener = new DOMNodeRemovedListener();
0542: et.addEventListener("DOMNodeRemoved",
0543: domNodeRemovedListener, false);
0544: domSubtreeModifiedListener = new DOMSubtreeModifiedListener();
0545: et.addEventListener("DOMSubtreeModified",
0546: domSubtreeModifiedListener, false);
0547: domCharacterDataModifiedListener = new DOMCharacterDataModifiedListener();
0548: et.addEventListener("DOMCharacterDataModified",
0549: domCharacterDataModifiedListener, false);
0550: styleDeclarationUpdateHandler = new StyleDeclarationUpdateHandler();
0551: }
0552:
0553: ALL_PROPERTIES = new int[getNumberOfProperties()];
0554: for (int i = getNumberOfProperties() - 1; i >= 0; --i) {
0555: ALL_PROPERTIES[i] = i;
0556: }
0557: }
0558:
0559: /**
0560: * Disposes the CSSEngine and all the attached resources.
0561: */
0562: public void dispose() {
0563: setCSSEngineUserAgent(null);
0564: disposeStyleMaps(document.getDocumentElement());
0565: if (document instanceof EventTarget) {
0566: // Detach the mutation events listeners.
0567: EventTarget et = (EventTarget) document;
0568: et.removeEventListener("DOMAttrModified",
0569: domAttrModifiedListener, false);
0570: et.removeEventListener("DOMNodeInserted",
0571: domNodeInsertedListener, false);
0572: et.removeEventListener("DOMNodeRemoved",
0573: domNodeRemovedListener, false);
0574: et.removeEventListener("DOMSubtreeModified",
0575: domSubtreeModifiedListener, false);
0576: et.removeEventListener("DOMCharacterDataModified",
0577: domCharacterDataModifiedListener, false);
0578: }
0579: }
0580:
0581: // <rave>
0582: // BEGIN RAVE MODIFICATIONS
0583: // private void disposeStyleMaps(Node node) {
0584: protected void disposeStyleMaps(Node node) {
0585: // END RAVE MODIFICATIONS
0586: // </rave>
0587: if (node instanceof CSSStylableElement) {
0588: ((CSSStylableElement) node).setComputedStyleMap(null, null);
0589: }
0590: for (Node n = node.getFirstChild(); n != null; n = n
0591: .getNextSibling()) {
0592: if (n.getNodeType() == Node.ELEMENT_NODE) {
0593: disposeStyleMaps(n);
0594: }
0595: Node c = getImportedChild(n);
0596: if (c != null) {
0597: disposeStyleMaps(c);
0598: }
0599: }
0600: }
0601:
0602: /**
0603: * Returns the CSS context.
0604: */
0605: public CSSContext getCSSContext() {
0606: return cssContext;
0607: }
0608:
0609: /**
0610: * Returns the document associated with this engine.
0611: */
0612: public Document getDocument() {
0613: return document;
0614: }
0615:
0616: /**
0617: * Returns the font-size property index.
0618: */
0619: public int getFontSizeIndex() {
0620: return fontSizeIndex;
0621: }
0622:
0623: /**
0624: * Returns the line-height property index.
0625: */
0626: public int getLineHeightIndex() {
0627: return lineHeightIndex;
0628: }
0629:
0630: /**
0631: * Returns the color property index.
0632: */
0633: public int getColorIndex() {
0634: return colorIndex;
0635: }
0636:
0637: /**
0638: * Returns the number of properties.
0639: */
0640: public int getNumberOfProperties() {
0641: return valueManagers.length;
0642: }
0643:
0644: /**
0645: * Returns the property index, or -1.
0646: */
0647: public int getPropertyIndex(String name) {
0648: return indexes.get(name);
0649: }
0650:
0651: /**
0652: * Returns the shorthand property index, or -1.
0653: */
0654: public int getShorthandIndex(String name) {
0655: return shorthandIndexes.get(name);
0656: }
0657:
0658: /**
0659: * Returns the name of the property at the given index.
0660: */
0661: public String getPropertyName(int idx) {
0662: return valueManagers[idx].getPropertyName();
0663: }
0664:
0665: public void setCSSEngineUserAgent(CSSEngineUserAgent userAgent) {
0666: this .userAgent = userAgent;
0667: }
0668:
0669: public CSSEngineUserAgent getCSSEngineUserAgent() {
0670: return userAgent;
0671: }
0672:
0673: /**
0674: * Sets the user agent style-sheet.
0675: */
0676: public void setUserAgentStyleSheet(StyleSheet ss) {
0677: userAgentStyleSheet = ss;
0678: }
0679:
0680: /**
0681: * Sets the user style-sheet.
0682: */
0683: public void setUserStyleSheet(StyleSheet ss) {
0684: userStyleSheet = ss;
0685: }
0686:
0687: /**
0688: * Returns the ValueManagers.
0689: */
0690: public ValueManager[] getValueManagers() {
0691: return valueManagers;
0692: }
0693:
0694: /**
0695: * Sets the media to use to compute the styles.
0696: */
0697: public void setMedia(String str) {
0698: try {
0699: media = parser.parseMedia(str);
0700: } catch (Exception e) {
0701: String m = e.getMessage();
0702: if (m == null)
0703: m = "";
0704: String s = Messages.formatMessage("media.error",
0705: new Object[] { str, m });
0706: throw new DOMException(DOMException.SYNTAX_ERR, s);
0707: }
0708: }
0709:
0710: /**
0711: * Sets the alternate style-sheet title.
0712: */
0713: public void setAlternateStyleSheet(String str) {
0714: alternateStyleSheet = str;
0715: }
0716:
0717: /**
0718: * Recursively imports the cascaded style from a source element
0719: * to an element of the current document.
0720: */
0721: public void importCascadedStyleMaps(Element src, CSSEngine srceng,
0722: Element dest) {
0723: if (src instanceof CSSStylableElement) {
0724: CSSStylableElement csrc = (CSSStylableElement) src;
0725: CSSStylableElement cdest = (CSSStylableElement) dest;
0726:
0727: StyleMap sm = srceng.getCascadedStyleMap(csrc, null);
0728: sm.setFixedCascadedStyle(true);
0729: cdest.setComputedStyleMap(null, sm);
0730:
0731: if (pseudoElementNames != null) {
0732: int len = pseudoElementNames.length;
0733: for (int i = 0; i < len; i++) {
0734: String pe = pseudoElementNames[i];
0735: sm = srceng.getCascadedStyleMap(csrc, pe);
0736: cdest.setComputedStyleMap(pe, sm);
0737: }
0738: }
0739: }
0740:
0741: for (Node dn = dest.getFirstChild(), sn = src.getFirstChild(); dn != null; dn = dn
0742: .getNextSibling(), sn = sn.getNextSibling()) {
0743: if (sn.getNodeType() == Node.ELEMENT_NODE) {
0744: importCascadedStyleMaps((Element) sn, srceng,
0745: (Element) dn);
0746: }
0747: }
0748: }
0749:
0750: /**
0751: * Returns the current base-url.
0752: */
0753: public URL getCSSBaseURI() {
0754: if (cssBaseURI == null) {
0755: // <rave>
0756: // BEGIN RAVE MODIFICATIONS
0757: // We sometimes call the parser when there's no URI available (for example
0758: // as part of the CSS string parsing service.) Make sure this isn't a
0759: // big problem by providing a silly URL - resources won't be found in this
0760: // case. I should consider requiring a URL for the CSS parse requests...
0761: //cssBaseURI = element.getCSSBase();
0762: if (element == null) {
0763: // No valid URL (such as at style editing time when we're just
0764: // editing a stylesheet
0765: // Use dummy
0766: try {
0767: return new URL("http", "localhost", 80, "dummy");
0768: } catch (MalformedURLException mfue) {
0769: }
0770: } else {
0771: cssBaseURI = element.getCSSBase();
0772: }
0773: // ELSE RAVE MODIFICATIONS
0774: // </rave>
0775: }
0776: return cssBaseURI;
0777: }
0778:
0779: /**
0780: * Returns the cascaded style of the given element/pseudo-element.
0781: * @param elt The stylable element.
0782: * @param pseudo Optional pseudo-element string (null if none).
0783: */
0784: public StyleMap getCascadedStyleMap(CSSStylableElement elt,
0785: String pseudo) {
0786: int props = getNumberOfProperties();
0787: StyleMap result = new StyleMap(props);
0788:
0789: // Apply the user-agent style-sheet to the result.
0790: if (userAgentStyleSheet != null) {
0791: List rules = new ArrayList();
0792: addMatchingRules(rules, userAgentStyleSheet, elt, pseudo);
0793: addRules(elt, pseudo, result, rules,
0794: StyleMap.USER_AGENT_ORIGIN);
0795: }
0796:
0797: // Apply the user properties style-sheet to the result.
0798: if (userStyleSheet != null) {
0799: List rules = new ArrayList();
0800: addMatchingRules(rules, userStyleSheet, elt, pseudo);
0801: addRules(elt, pseudo, result, rules, StyleMap.USER_ORIGIN);
0802: }
0803:
0804: element = elt;
0805: try {
0806: // Apply the non-CSS presentational hints to the result.
0807: // <rave>
0808: // BEGIN RAVE MODIFICATIONS
0809: applyNonCSSPresentationalHints(elt, result);
0810: // This support was useless, it was simply aliasing CSS names
0811: // to properties! But usually it's not a straight map!
0812: // For example, the HTML attribute "background" maps to the
0813: // CSS property "background-image" ! And the attribute "bgcolor"
0814: // maps to the CSS property "background-color" !
0815: // So instead we'll handle this in subclasses where specific
0816: // logic can be written (the attribute value often has to be
0817: // special handled too so I can't just provide a map to CSSEngine;
0818: // e.g. for "background" for example I need to change the string
0819: // value into a URI.)
0820: // if (nonCSSPresentationalHints != null) {
0821: // NamedNodeMap attrs = elt.getAttributes();
0822: // int len = attrs.getLength();
0823: // for (int i = 0; i < len; i++) {
0824: // Node attr = attrs.item(i);
0825: // String an = attr.getNodeName();
0826: // if (nonCSSPresentationalHints.contains(an)) {
0827: // try {
0828: // LexicalUnit lu;
0829: // int idx = getPropertyIndex(an);
0830: // lu = parser.parsePropertyValue
0831: // (attr.getNodeValue());
0832: // ValueManager vm = valueManagers[idx];
0833: // Value v = vm.createValue(lu, this);
0834: // putAuthorProperty(result, idx, v, false,
0835: // StyleMap.NON_CSS_ORIGIN);
0836: // } catch (Exception e) {
0837: // String m = e.getMessage();
0838: // if (m == null) m = "";
0839: // String u = ((documentURI == null)?"<unknown>":
0840: // documentURI.toString());
0841: // String s = Messages.formatMessage
0842: // ("property.syntax.error.at",
0843: // new Object[] { u, an, attr.getNodeValue(),m});
0844: // DOMException de = new DOMException(DOMException.SYNTAX_ERR, s);
0845: // de.initCause(e);
0846: // if (userAgent == null) throw de;
0847: // userAgent.displayError(de);
0848: // }
0849: // }
0850: // }
0851: // }
0852: // END RAVE MODIFICATIONS
0853: // </rave>
0854:
0855: // Apply the document style-sheets to the result.
0856: // <nb
0857: // List snodes = getStyleSheetNodes();
0858: // ===
0859: List<WeakReference<CSSStyleSheetNode>> snodes = getStyleSheetNodes();
0860: // </nb>
0861: int slen = snodes.size();
0862: if (slen > 0) {
0863: List rules = new ArrayList();
0864: for (int i = 0; i < slen; i++) {
0865: // <nb>
0866: // CSSStyleSheetNode ssn = (CSSStyleSheetNode)snodes.get(i);
0867: // ===
0868: WeakReference<CSSStyleSheetNode> ssnWRef = snodes
0869: .get(i);
0870: CSSStyleSheetNode ssn = ssnWRef == null ? null
0871: : ssnWRef.get();
0872: if (ssn == null) {
0873: continue;
0874: }
0875: // </nb>
0876: StyleSheet ss = ssn.getCSSStyleSheet();
0877: if (ss != null
0878: && (!ss.isAlternate()
0879: || ss.getTitle() == null || ss
0880: .getTitle().equals(
0881: alternateStyleSheet))
0882: && mediaMatch(ss.getMedia())) {
0883: addMatchingRules(rules, ss, elt, pseudo);
0884: }
0885: }
0886: addRules(elt, pseudo, result, rules,
0887: StyleMap.AUTHOR_ORIGIN);
0888: }
0889: // <rave>
0890: // BEGIN RAVE MODIFICATIONS
0891: // Check if we have any transient stylesheet nodes to
0892: // consider
0893: if (transientStyleSheetNodes != null) {
0894: List rules = new ArrayList(transientStyleSheetNodes
0895: .size());
0896: for (int i = 0; i < transientStyleSheetNodes.size(); i++) {
0897: CSSStyleSheetNode ssn = (CSSStyleSheetNode) transientStyleSheetNodes
0898: .get(i);
0899: StyleSheet ss = ssn.getCSSStyleSheet();
0900: if (ss != null
0901: && (!ss.isAlternate()
0902: || ss.getTitle() == null || ss
0903: .getTitle().equals(
0904: alternateStyleSheet))
0905: && mediaMatch(ss.getMedia())) {
0906: addMatchingRules(rules, ss, elt, pseudo);
0907: }
0908: }
0909: addRules(elt, pseudo, result, rules,
0910: StyleMap.AUTHOR_ORIGIN);
0911: }
0912: // END RAVE MODIFICATIONS
0913: // </rave>
0914:
0915: // Apply the inline style to the result.
0916: if (styleLocalName != null) {
0917: String style = elt.getAttributeNS(styleNamespaceURI,
0918: styleLocalName);
0919: if (style.length() > 0) {
0920: try {
0921: parser
0922: .setSelectorFactory(CSSSelectorFactory.INSTANCE);
0923: parser.setConditionFactory(cssConditionFactory);
0924: styleDeclarationDocumentHandler.styleMap = result;
0925: parser
0926: .setDocumentHandler(styleDeclarationDocumentHandler);
0927: // <rave>
0928: // BEGIN RAVE MODIFICATIONS
0929: styleDeclarationDocumentHandler.location = elt;
0930: styleDeclarationDocumentHandler.lineno = -1;
0931: // END RAVE MODIFICATIONS
0932: // </rave>
0933: parser.parseStyleDeclaration(style);
0934: styleDeclarationDocumentHandler.styleMap = null;
0935: } catch (Exception e) {
0936: String m = e.getMessage();
0937: if (m == null)
0938: m = "";
0939: String u = ((documentURI == null) ? "<unknown>"
0940: : documentURI.toString());
0941: String s = Messages.formatMessage(
0942: "style.syntax.error.at", new Object[] {
0943: u, styleLocalName, style, m });
0944: DOMException de = new DOMException(
0945: DOMException.SYNTAX_ERR, s);
0946: // <rave>
0947: // BEGIN RAVE MODIFICATIONS
0948: de.initCause(e);
0949: // END RAVE MODIFICATIONS
0950: // </rave>
0951: if (userAgent == null)
0952: throw de;
0953: userAgent.displayError(de);
0954: }
0955: }
0956: }
0957: } finally {
0958: element = null;
0959: cssBaseURI = null;
0960: }
0961:
0962: return result;
0963: }
0964:
0965: // <rave>
0966: // BEGIN RAVE MODIFICATIONS
0967: /** Some error occurred while parsing stylesheet - display it to
0968: * the developer */
0969: protected void displayError(DOMException e, Object location,
0970: int lineno, int column) {
0971: // Overridden in subclasses
0972: }
0973:
0974: protected void displayMissingStyleSheet(String uri) {
0975: // Overridden in subclasses
0976: }
0977:
0978: /**
0979: * Shorthand property being set. Set when we're processing a shorthand property
0980: * such that individual value managers can figure out that they're part of a shorthand
0981: * property expansion (useful when creating error messages for example
0982: */
0983: private int expandingShorthandProperty = -1;
0984:
0985: /**
0986: * Return the shorthand property being set. Set when we're processing a shorthand property
0987: * such that individual value managers can figure out that they're part of a shorthand
0988: * property expansion (useful when creating error messages for example. Returns null
0989: * if no shorthand property is being processed.
0990: */
0991: public String getExpandingShorthandProperty() {
0992: if (expandingShorthandProperty == -1) {
0993: return null;
0994: } else {
0995: return shorthandManagers[expandingShorthandProperty]
0996: .getPropertyName();
0997: }
0998: }
0999:
1000: /** Set the given element's value for a given attribute; if value is
1001: * null, remove the attribute. */
1002: protected void setAttributeValue(Element elt, String name,
1003: String value) {
1004: // This sometimes throws ClassCastExceptions in Xerces -- why?
1005: //elt.setAttributeNS(styleNamespaceURI, styleLocalName, value);
1006: if (value != null) {
1007: elt.setAttribute(name, value);
1008: } else {
1009: elt.removeAttribute(name);
1010: }
1011: /* CAST: Ah I think I know now: in the method they have this comment:
1012:
1013: // This case may happen if user calls:
1014: // elem.setAttribute("name", "value");
1015: // elem.setAttributeNS(null, "name", "value");
1016: // This case is not defined by the DOM spec, we choose
1017: // to create a new attribute in this case and remove an old one from the tree
1018: // note this might cause events to be propagated or user data to be lost
1019:
1020: and then they proceed to cast to a CoreDocumentImpl... I bet
1021: it's just not safe to mix setAttribute and setAttributeNS and
1022: that's what we must have been doing ... so let's just switch.
1023: */
1024: }
1025:
1026: /**
1027: * Scan attributes for an element and transcribe deprecated
1028: * style-type attributes into real style properties
1029: */
1030: protected void applyNonCSSPresentationalHints(
1031: CSSStylableElement elt, StyleMap map) {
1032: // Overridden in subclasses
1033: }
1034:
1035: /**
1036: * For use by the implementation of applyNonCSSPresentationalHints;
1037: * apply a non-presentational CSS hint as the given CSS value for
1038: * the given CSS property.
1039: */
1040: protected void applyNonCSSPresentationalHint(
1041: CSSStylableElement elt, StyleMap map, int idx, String value) {
1042: try {
1043: LexicalUnit lu = parser.parsePropertyValue(value);
1044: ValueManager vm = valueManagers[idx];
1045: try {
1046: Value v = vm.createValue(lu, this );
1047:
1048: if (v instanceof AbstractValue) { // Should I add to Value interface???
1049: AbstractValue av = (AbstractValue) v;
1050: av.setLocation(elt);
1051: av.setLineNumber(-1);
1052: }
1053:
1054: putAuthorProperty(map, idx, v, false,
1055: StyleMap.NON_CSS_ORIGIN);
1056: } catch (DOMException e) {
1057: // Something bad happened
1058: displayError(e, elt, 0, 0);
1059: // Continue processing - we're supposed to ignore
1060: // errant declarations!!!
1061: }
1062:
1063: } catch (Exception e) {
1064: // No longer used???
1065: String m = e.getMessage();
1066: if (m == null)
1067: m = "";
1068: String u = ((documentURI == null) ? "<unknown>"
1069: : documentURI.toString());
1070: String s = Messages.formatMessage // TODO: make our own message here
1071: ("property.syntax.error.at", new Object[] { u, "?",
1072: value, m });
1073: DOMException de = new DOMException(DOMException.SYNTAX_ERR,
1074: s);
1075: de.initCause(e);
1076: if (userAgent == null)
1077: throw de;
1078: userAgent.displayError(de);
1079: }
1080: }
1081:
1082: /**
1083: * For use by the implementation of applyNonCSSPresentationalHints;
1084: * apply a non-presentational CSS hint as the given CSS value for
1085: * the given CSS property.
1086: */
1087: protected void applyNonCSSPresentationalHint(
1088: CSSStylableElement elt, StyleMap map, int idx, Value v) {
1089: ValueManager vm = valueManagers[idx];
1090: try {
1091: putAuthorProperty(map, idx, v, false,
1092: StyleMap.NON_CSS_ORIGIN);
1093: } catch (DOMException e) {
1094: // Something bad happened
1095: displayError(e, elt, 0, 0);
1096: // Continue processing - we're supposed to ignore
1097: // errant declarations!!!
1098: }
1099: }
1100:
1101: /**
1102: * Return true iff the given CSS property for the given element
1103: * is not set, e.g. the default value would be returned instead.
1104: * Note - it's not the same as asking if the property has the same
1105: * value as its default property; this method will still return false
1106: * in that case since it has a value set, so it doesn't have to pull
1107: * the default value.
1108: */
1109: public boolean isDefaultValue(CSSStylableElement elt,
1110: String pseudo, int propidx) {
1111: StyleMap sm = elt.getComputedStyleMap(pseudo);
1112: if (sm == null) {
1113: sm = getCascadedStyleMap(elt, pseudo);
1114: elt.setComputedStyleMap(pseudo, sm);
1115: }
1116:
1117: Value value = sm.getValue(propidx);
1118: if (value != null) {
1119: return false;
1120: }
1121: ValueManager vm = valueManagers[propidx];
1122: CSSStylableElement p = getParentCSSStylableElement(elt);
1123: if (value == null && (!vm.isInheritedProperty() || p == null)) {
1124: return true;
1125: }
1126: return false;
1127: }
1128:
1129: /**
1130: * Return true iff the given CSS property for the given element
1131: * was inherited, as opposed to referenced directly by some rule.
1132: */
1133: public boolean isInheritedValue(CSSStylableElement elt, int propidx) {
1134: // Make sure it's computed first
1135: Value v = getComputedStyle(elt, null, propidx);
1136: StyleMap sm = elt.getComputedStyleMap(null);
1137: return sm.isInherited(propidx);
1138: }
1139:
1140: /**
1141: * Return a list of style classes defined in the stylesheets for
1142: * this engine. This will not include any styleclasses defined in
1143: * the user agent stylesheet.
1144: */
1145: public Collection getStyleClasses() {
1146: //ArrayList styleClasses = new ArrayList();
1147: TreeSet styleClasses = new TreeSet();
1148:
1149: // We don't want to list any styleclasses in the user agent stylesheet
1150: if (userStyleSheet != null) {
1151: List rules = new ArrayList();
1152: addStyleClasses(styleClasses, userStyleSheet);
1153: }
1154:
1155: // Apply the document style-sheets to the result.
1156: // <nb>
1157: // List snodes = getStyleSheetNodes();
1158: // ===
1159: List<WeakReference<CSSStyleSheetNode>> snodes = getStyleSheetNodes();
1160: // </nb>
1161: int slen = snodes.size();
1162: if (slen > 0) {
1163: for (int i = 0; i < slen; i++) {
1164: // <nb>
1165: // CSSStyleSheetNode ssn = (CSSStyleSheetNode)snodes.get(i);
1166: // ===
1167: WeakReference<CSSStyleSheetNode> ssnWRef = snodes
1168: .get(i);
1169: CSSStyleSheetNode ssn = ssnWRef == null ? null
1170: : ssnWRef.get();
1171: if (ssn == null) {
1172: continue;
1173: }
1174: // </nb>
1175: StyleSheet ss = ssn.getCSSStyleSheet();
1176: if (ss != null
1177: && (!ss.isAlternate() || ss.getTitle() == null || ss
1178: .getTitle().equals(alternateStyleSheet))
1179: && mediaMatch(ss.getMedia())) {
1180: addStyleClasses(styleClasses, ss);
1181: }
1182: }
1183: }
1184: if (transientStyleSheetNodes != null) {
1185: for (int i = 0; i < transientStyleSheetNodes.size(); i++) {
1186: CSSStyleSheetNode ssn = (CSSStyleSheetNode) transientStyleSheetNodes
1187: .get(i);
1188: StyleSheet ss = ssn.getCSSStyleSheet();
1189: if (ss != null
1190: && (!ss.isAlternate() || ss.getTitle() == null || ss
1191: .getTitle().equals(alternateStyleSheet))
1192: && mediaMatch(ss.getMedia())) {
1193: addStyleClasses(styleClasses, ss);
1194: }
1195: }
1196: }
1197: // Suppress duplicates
1198: return styleClasses;
1199: }
1200:
1201: private void addStyleClasses(Collection styleClasses, StyleSheet ss) {
1202: int len = ss.getSize();
1203: for (int i = 0; i < len; i++) {
1204: Rule r = ss.getRule(i);
1205: switch (r.getType()) {
1206: case StyleRule.TYPE:
1207: StyleRule style = (StyleRule) r;
1208: SelectorList sl = style.getSelectorList();
1209: int slen = sl.getLength();
1210: for (int j = 0; j < slen; j++) {
1211: ExtendedSelector s = (ExtendedSelector) sl.item(j);
1212: if (s instanceof CSSConditionalSelector) {
1213: CSSConditionalSelector cs = (CSSConditionalSelector) s;
1214: if (cs.getCondition() instanceof CSSClassCondition) {
1215: CSSClassCondition ck = (CSSClassCondition) cs
1216: .getCondition();
1217: styleClasses.add(ck.getValue());
1218: }
1219: }
1220: }
1221: break;
1222:
1223: case MediaRule.TYPE:
1224: case ImportRule.TYPE:
1225: MediaRule mr = (MediaRule) r;
1226: if (mediaMatch(mr.getMediaList())) {
1227: addStyleClasses(styleClasses, mr);
1228: }
1229: break;
1230: }
1231: }
1232: }
1233:
1234: /** Return a display of the matching rules for the given element */
1235: public String getMatchingRules(Element elt,
1236: boolean includeAgentRules) {
1237: String pseudo = "";
1238: StringBuffer sb = new StringBuffer(2000);
1239:
1240: if (includeAgentRules) {
1241: // Apply the user-agent style-sheet to the result.
1242: if (userAgentStyleSheet != null) {
1243: List rules = new ArrayList();
1244: addMatchingRules(rules, userAgentStyleSheet, elt,
1245: pseudo);
1246: Iterator it = rules.iterator();
1247: if (it.hasNext()) {
1248: sb.append("Default User Agent Styles:\n");
1249: }
1250: while (it.hasNext()) {
1251: Rule rule = (Rule) it.next();
1252: sb.append(rule.toString(this ));
1253: sb.append("\n");
1254: }
1255: }
1256: }
1257:
1258: // Apply the user properties style-sheet to the result.
1259: if (userStyleSheet != null) {
1260: List rules = new ArrayList();
1261: addMatchingRules(rules, userStyleSheet, elt, pseudo);
1262: Iterator it = rules.iterator();
1263: if (it.hasNext()) {
1264: sb.append("Default User Styles:\n");
1265: }
1266: while (it.hasNext()) {
1267: Rule rule = (Rule) it.next();
1268: sb.append(rule.toString(this ));
1269: sb.append("\n");
1270: }
1271: }
1272:
1273: // Apply the document style-sheets to the result.
1274: // <nb>
1275: // List snodes = getStyleSheetNodes();
1276: // ===
1277: List<WeakReference<CSSStyleSheetNode>> snodes = getStyleSheetNodes();
1278: // </nb>
1279: int slen = snodes.size();
1280: if (slen > 0) {
1281: for (int i = 0; i < slen; i++) {
1282: // <nb>
1283: // CSSStyleSheetNode ssn = (CSSStyleSheetNode)snodes.get(i);
1284: // ===
1285: WeakReference<CSSStyleSheetNode> ssnWRef = snodes
1286: .get(i);
1287: CSSStyleSheetNode ssn = ssnWRef == null ? null
1288: : ssnWRef.get();
1289: if (ssn == null) {
1290: continue;
1291: }
1292: // </nb>
1293: StyleSheet ss = ssn.getCSSStyleSheet();
1294: if (ss != null
1295: && (!ss.isAlternate() || ss.getTitle() == null || ss
1296: .getTitle().equals(alternateStyleSheet))
1297: && mediaMatch(ss.getMedia())) {
1298: List rules = new ArrayList();
1299: addMatchingRules(rules, ss, elt, pseudo);
1300: Iterator it = rules.iterator();
1301: if (it.hasNext()) {
1302: sb.append(ss.getTitle() + ":\n");
1303: }
1304: while (it.hasNext()) {
1305: Rule rule = (Rule) it.next();
1306: sb.append(rule.toString(this ));
1307: sb.append("\n");
1308: }
1309: }
1310: }
1311: }
1312:
1313: // Check if we have any transient stylesheet nodes to
1314: // consider
1315: if (transientStyleSheetNodes != null) {
1316: for (int i = 0; i < transientStyleSheetNodes.size(); i++) {
1317: CSSStyleSheetNode ssn = (CSSStyleSheetNode) transientStyleSheetNodes
1318: .get(i);
1319: StyleSheet ss = ssn.getCSSStyleSheet();
1320: if (ss != null
1321: && (!ss.isAlternate() || ss.getTitle() == null || ss
1322: .getTitle().equals(alternateStyleSheet))
1323: && mediaMatch(ss.getMedia())) {
1324: List rules = new ArrayList();
1325: addMatchingRules(rules, ss, elt, pseudo);
1326: Iterator it = rules.iterator();
1327: if (it.hasNext()) {
1328: sb.append(ss.getTitle() + ":\n");
1329: }
1330: while (it.hasNext()) {
1331: Rule rule = (Rule) it.next();
1332: sb.append(rule.toString(this ));
1333: sb.append("\n");
1334: }
1335: }
1336: }
1337: }
1338:
1339: String style = elt.getAttribute(styleLocalName);
1340: if (style.length() > 0) {
1341: sb.append("style=\"" + style + "\"\n");
1342: }
1343:
1344: return sb.toString();
1345: }
1346:
1347: /**
1348: * Updates the local styles for an element. First any style settings
1349: * in the set parameter array are applied, then any properties
1350: * pointed to by the remove array are applied.
1351: * @return The new style string
1352: */
1353: public String getUpdatedLocalStyleValues(CSSStylableElement elt,
1354: StyleSetting[] stylesToSet, StyleSetting[] stylesToRemove)
1355: throws Exception {
1356: String style = elt.getAttributeNS(styleNamespaceURI,
1357: styleLocalName);
1358: StringBuffer styleBuffer = new StringBuffer(200);
1359: if (style.length() > 0) {
1360: styleBuffer.append(style);
1361: }
1362: if (stylesToSet != null) {
1363: for (int i = 0; i < stylesToSet.length; i++) {
1364: StyleSetting setting = stylesToSet[i];
1365: if (styleBuffer.length() > 0) {
1366: styleBuffer.append("; ");
1367: }
1368: styleBuffer.append(getPropertyName(setting.getIndex()));
1369: styleBuffer.append(":");
1370: styleBuffer.append(setting.getValue());
1371: }
1372: }
1373: style = styleBuffer.toString();
1374:
1375: CSSStylableElement old = element;
1376: try {
1377: int props = getNumberOfProperties();
1378: StyleMap result = new StyleMap(props);
1379: element = (CSSStylableElement) elt;
1380:
1381: unknownPropertyNames = new ArrayList();
1382: unknownPropertyValues = new ArrayList();
1383: parser.setSelectorFactory(CSSSelectorFactory.INSTANCE);
1384: parser.setConditionFactory(cssConditionFactory);
1385: styleDeclarationDocumentHandler.styleMap = result;
1386: parser.setDocumentHandler(styleDeclarationDocumentHandler);
1387: styleDeclarationDocumentHandler.location = elt;
1388: styleDeclarationDocumentHandler.lineno = -1;
1389: parser.parseStyleDeclaration(style);
1390: styleDeclarationDocumentHandler.styleMap = null;
1391:
1392: // Perform deletions
1393: if (stylesToRemove != null) {
1394: StyleMap sm = elt.getComputedStyleMap(null); // XXX what do we use for pseudo?
1395: for (int i = 0; i < stylesToRemove.length; i++) {
1396: StyleSetting setting = stylesToRemove[i];
1397: // Remove properties
1398: result.putValue(setting.getIndex(), null);
1399: if (sm != null) {
1400: sm.putValue(setting.getIndex(), null);
1401: }
1402: }
1403: }
1404:
1405: // Get stylemap back as a string...
1406: //String s = result.toStyleString(this);
1407: String s = toMinimalStyleString(result);
1408:
1409: // Append "unknown" styles
1410: if (unknownPropertyNames.size() > 0) {
1411: StringBuffer sb = new StringBuffer(s.length()
1412: + unknownPropertyNames.size() * 30);
1413: sb.append(s);
1414: for (int i = 0, n = unknownPropertyNames.size(); i < n; i++) {
1415: if (sb.length() > 0) {
1416: sb.append(';');
1417: sb.append(' ');
1418: }
1419: sb.append(unknownPropertyNames.get(i).toString());
1420: sb.append(':');
1421: sb.append(' ');
1422: sb.append(unknownPropertyValues.get(i).toString());
1423: }
1424: s = sb.toString();
1425: }
1426:
1427: if (s.length() == 0) {
1428: s = null;
1429: }
1430:
1431: // <removing design bean manipulation in engine>
1432: // setAttributeValue(elt, styleLocalName, s);
1433: // </removing design bean manipulation in engine>
1434:
1435: return s;
1436:
1437: } catch (Exception e) {
1438: String m = e.getMessage();
1439: if (m == null)
1440: m = "";
1441: String u = ((documentURI == null) ? "<unknown>"
1442: : documentURI.toString());
1443: String s = Messages.formatMessage("style.syntax.error.at",
1444: new Object[] { u, styleLocalName, style, m });
1445: DOMException de = new DOMException(DOMException.SYNTAX_ERR,
1446: s);
1447: de.initCause(e);
1448: if (userAgent == null)
1449: throw de;
1450: userAgent.displayError(de);
1451:
1452: // <removing design bean manipulation in engine>
1453: // return null;
1454: // ====
1455: throw de;
1456: // </removing design bean manipulation in engine>
1457: } finally {
1458: unknownPropertyNames = null;
1459: unknownPropertyValues = null;
1460: element = old;
1461: }
1462: }
1463:
1464: /** When non null, stores a list of properties that were not recognized.
1465: * This is only currently done for parsing of style expressions, not style
1466: * sheets, since it's used for local style element manipulation.
1467: */
1468: protected List unknownPropertyNames;
1469: protected List unknownPropertyValues;
1470:
1471: /** Given a lexical unit value, produce its CSS value expression
1472: * string by appending into the given StringBuffer. This is a
1473: * "toString()" like method I think LexicalUnit should have
1474: * offered. I really just want the "r-value" from a style
1475: * declaration, but the parser doesn't keep it around so we have to
1476: * reconstruct it from lexical tokens.
1477: */
1478: private void appendCss(StringBuffer sb, LexicalUnit value) {
1479: while (value != null) {
1480: switch (value.getLexicalUnitType()) {
1481:
1482: case LexicalUnit.SAC_OPERATOR_COMMA:
1483: sb.append(",");
1484: break; // NOI18N
1485: case LexicalUnit.SAC_OPERATOR_PLUS:
1486: sb.append("+");
1487: break; // NOI18N
1488: case LexicalUnit.SAC_OPERATOR_MINUS:
1489: sb.append("-");
1490: break; // NOI18N
1491: case LexicalUnit.SAC_OPERATOR_MULTIPLY:
1492: sb.append("*");
1493: break; // NOI18N
1494: case LexicalUnit.SAC_OPERATOR_SLASH:
1495: sb.append("/");
1496: break; // NOI18N
1497: case LexicalUnit.SAC_OPERATOR_MOD:
1498: sb.append("%");
1499: break; // NOI18N
1500: case LexicalUnit.SAC_OPERATOR_EXP:
1501: sb.append("^");
1502: break; // NOI18N
1503: case LexicalUnit.SAC_OPERATOR_LT:
1504: sb.append("<");
1505: break; // NOI18N
1506: case LexicalUnit.SAC_OPERATOR_GT:
1507: sb.append(">");
1508: break; // NOI18N
1509: case LexicalUnit.SAC_OPERATOR_LE:
1510: sb.append("<=");
1511: break; // NOI18N
1512: case LexicalUnit.SAC_OPERATOR_GE:
1513: sb.append(">=");
1514: break; // NOI18N
1515: case LexicalUnit.SAC_OPERATOR_TILDE:
1516: sb.append("~");
1517: break; // NOI18N
1518: case LexicalUnit.SAC_INHERIT:
1519: sb.append("inherit");
1520: break; // NOI18N
1521: case LexicalUnit.SAC_INTEGER:
1522: sb.append(Integer.toString(value.getIntegerValue()));
1523: break;
1524: case LexicalUnit.SAC_REAL:
1525: sb.append(Float.toString(value.getFloatValue()));
1526: break;
1527: case LexicalUnit.SAC_EM:
1528: sb.append(Float.toString(value.getFloatValue())
1529: + value.getDimensionUnitText());
1530: break;
1531: case LexicalUnit.SAC_EX:
1532: sb.append(Float.toString(value.getFloatValue())
1533: + value.getDimensionUnitText());
1534: break;
1535: case LexicalUnit.SAC_PIXEL:
1536: sb.append(Float.toString(value.getFloatValue())
1537: + value.getDimensionUnitText());
1538: break;
1539: case LexicalUnit.SAC_INCH:
1540: sb.append(Float.toString(value.getFloatValue())
1541: + value.getDimensionUnitText());
1542: break;
1543: case LexicalUnit.SAC_CENTIMETER:
1544: sb.append(Float.toString(value.getFloatValue())
1545: + value.getDimensionUnitText());
1546: break;
1547: case LexicalUnit.SAC_MILLIMETER:
1548: sb.append(Float.toString(value.getFloatValue())
1549: + value.getDimensionUnitText());
1550: break;
1551: case LexicalUnit.SAC_POINT:
1552: sb.append(Float.toString(value.getFloatValue())
1553: + value.getDimensionUnitText());
1554: break;
1555: case LexicalUnit.SAC_PICA:
1556: sb.append(Float.toString(value.getFloatValue())
1557: + value.getDimensionUnitText());
1558: break;
1559: case LexicalUnit.SAC_PERCENTAGE:
1560: sb.append(Float.toString(value.getFloatValue())
1561: + value.getDimensionUnitText());
1562: break;
1563: case LexicalUnit.SAC_URI:
1564: sb.append("uri(" + value.getStringValue() + ")");
1565: break; // NOI18N
1566: case LexicalUnit.SAC_DEGREE:
1567: sb.append(Float.toString(value.getFloatValue())
1568: + value.getDimensionUnitText());
1569: break;
1570: case LexicalUnit.SAC_GRADIAN:
1571: sb.append(Float.toString(value.getFloatValue())
1572: + value.getDimensionUnitText());
1573: break;
1574: case LexicalUnit.SAC_RADIAN:
1575: sb.append(Float.toString(value.getFloatValue())
1576: + value.getDimensionUnitText());
1577: break;
1578: case LexicalUnit.SAC_MILLISECOND:
1579: sb.append(Float.toString(value.getFloatValue())
1580: + value.getDimensionUnitText());
1581: break;
1582: case LexicalUnit.SAC_SECOND:
1583: sb.append(Float.toString(value.getFloatValue())
1584: + value.getDimensionUnitText());
1585: break;
1586: case LexicalUnit.SAC_HERTZ:
1587: sb.append(Float.toString(value.getFloatValue())
1588: + value.getDimensionUnitText());
1589: break;
1590: case LexicalUnit.SAC_KILOHERTZ:
1591: sb.append(Float.toString(value.getFloatValue())
1592: + value.getDimensionUnitText());
1593: break;
1594: case LexicalUnit.SAC_IDENT:
1595: sb.append(value.getStringValue());
1596: break;
1597: case LexicalUnit.SAC_STRING_VALUE:
1598: sb.append("\"" + value.getStringValue() + "\"");
1599: break;
1600: case LexicalUnit.SAC_ATTR:
1601: sb.append(value.getStringValue());
1602: break;
1603: case LexicalUnit.SAC_DIMENSION:
1604: sb.append(Float.toString(value.getFloatValue()));
1605: break;
1606:
1607: // XXX not yet supported, SAC doesn't yet handle
1608: case LexicalUnit.SAC_UNICODERANGE:
1609: break; // NOI18N
1610:
1611: case LexicalUnit.SAC_SUB_EXPRESSION:
1612: sb.append("("); // NOI18N
1613: appendCss(sb, value.getSubValues());
1614: sb.append(")"); // NOI18N
1615: break;
1616:
1617: case LexicalUnit.SAC_RGBCOLOR:
1618: // TODO - convert to hex instead? e.g. #FFFFFF
1619: sb.append("rgb"); // NOI18N
1620: sb.append("("); // NOI18N
1621: appendCss(sb, value.getParameters());
1622: sb.append(")"); // NOI18N
1623: break;
1624: case LexicalUnit.SAC_COUNTER_FUNCTION:
1625: sb.append("counter"); // NOI18N
1626: sb.append("("); // NOI18N
1627: appendCss(sb, value.getParameters());
1628: sb.append(")"); // NOI18N
1629: break;
1630: case LexicalUnit.SAC_COUNTERS_FUNCTION:
1631: sb.append("counters"); // NOI18N
1632: sb.append("("); // NOI18N
1633: appendCss(sb, value.getParameters());
1634: sb.append(")"); // NOI18N
1635: break;
1636: case LexicalUnit.SAC_RECT_FUNCTION:
1637: sb.append("rect"); // NOI18N
1638: sb.append("("); // NOI18N
1639: appendCss(sb, value.getParameters());
1640: sb.append(")"); // NOI18N
1641: break;
1642: case LexicalUnit.SAC_FUNCTION:
1643: sb.append(value.getFunctionName());
1644: sb.append("("); // NOI18N
1645: appendCss(sb, value.getParameters());
1646: sb.append(")"); // NOI18N
1647: break;
1648: }
1649:
1650: value = value.getNextLexicalUnit();
1651: if (value != null) {
1652: sb.append(' ');
1653: }
1654: }
1655: }
1656:
1657: /** Notify the engine that a new style element has been
1658: * created
1659: */
1660: public void addTransientStyleSheetNode(CSSStyleSheetNode elt) {
1661: if (transientStyleSheetNodes == null) {
1662: transientStyleSheetNodes = new ArrayList();
1663: }
1664: transientStyleSheetNodes.add(elt);
1665: }
1666:
1667: public void clearTransientStyleSheetNodes() {
1668: transientStyleSheetNodes = null;
1669: }
1670:
1671: private ArrayList transientStyleSheetNodes;
1672:
1673: protected void warnCircularReference(URL uri, Object location) {
1674: System.err.println("Circular reference: " + uri);
1675: }
1676:
1677: /** For statistics gathering during development */
1678: public static int styleLookupCount = 0;
1679:
1680: /**
1681: * Constant which indicates that the given stylesheet is being parsed. Used
1682: * to avoid circular references.
1683: */
1684: public final static StyleSheet PARSING_SHEET = new StyleSheet();
1685:
1686: // END RAVE MODIFICATIONS
1687: // </rave>
1688: /**
1689: * Returns the computed style of the given element/pseudo for the
1690: * property corresponding to the given index.
1691: */
1692: public Value getComputedStyle(CSSStylableElement elt,
1693: String pseudo, int propidx) {
1694: // <rave>
1695: // BEGIN RAVE MODIFICATIONS
1696: styleLookupCount++;
1697: // END RAVE MODIFICATIONS
1698: // </rave>
1699:
1700: StyleMap sm = elt.getComputedStyleMap(pseudo);
1701: if (sm == null) {
1702: sm = getCascadedStyleMap(elt, pseudo);
1703: elt.setComputedStyleMap(pseudo, sm);
1704: }
1705:
1706: Value value = sm.getValue(propidx);
1707: if (!sm.isComputed(propidx)) {
1708: Value result = value;
1709: ValueManager vm = valueManagers[propidx];
1710: CSSStylableElement p = getParentCSSStylableElement(elt);
1711: if (value == null
1712: && (!vm.isInheritedProperty() || p == null)) {
1713: result = vm.getDefaultValue();
1714: } else if (value != null
1715: && (value == InheritValue.INSTANCE) && p != null) {
1716: result = null;
1717: }
1718: if (result == null) {
1719: // Value is 'inherit' and p != null.
1720: // The pseudo class is not propagated.
1721: result = getComputedStyle(p, null, propidx);
1722: sm.putParentRelative(propidx, true);
1723: // <rave>
1724: // BEGIN RAVE MODIFICATIONS
1725: sm.putInherited(propidx, true);
1726: // END RAVE MODIFICATIONS
1727: // </rave>
1728: } else {
1729: // Maybe is it a relative value.
1730: result = vm.computeValue(elt, pseudo, this , propidx,
1731: sm, result);
1732: }
1733: if (value == null) {
1734: sm.putValue(propidx, result);
1735: sm.putNullCascaded(propidx, true);
1736: // <rave>
1737: // BEGIN RAVE MODIFICATIONS
1738: } else if (value == InheritValue.INSTANCE) {
1739: // Do nothing; rather than produce a new ComputedValue,
1740: // just inherit the value directly. That way we don't
1741: // end up with "wrapped" versions of values that are
1742: // part of an IDENT list, such as Vertical Alignments.
1743: // This breaks checking value == CssValueConstants.FOO_VALUE
1744: // for example.
1745: sm.putValue(propidx, result);
1746: sm.putInherited(propidx, true);
1747: // END RAVE MODIFICATIONS
1748: // </rave>
1749: } else if (result != value) {
1750: ComputedValue cv = new ComputedValue(value);
1751: cv.setComputedValue(result);
1752: sm.putValue(propidx, cv);
1753: result = cv;
1754: }
1755: sm.putComputed(propidx, true);
1756: value = result;
1757: }
1758: return value;
1759: }
1760:
1761: /**
1762: * Returns the document CSSStyleSheetNodes in a list. This list is
1763: * updated as the document is modified.
1764: */
1765: // <nb>
1766: // public List getStyleSheetNodes() {
1767: // ===
1768: public List<WeakReference<CSSStyleSheetNode>> getStyleSheetNodes() {
1769: // </nb>
1770: // XXX #126462 Checking style sheet nodes cache.
1771: boolean discardOldCache = false;
1772: if (styleSheetNodes != null) {
1773: for (WeakReference<CSSStyleSheetNode> styleSheetNodeWRef : styleSheetNodes) {
1774: CSSStyleSheetNode ssnode = styleSheetNodeWRef == null ? null
1775: : styleSheetNodeWRef.get();
1776: if (ssnode == null) {
1777: // The node was garbaged, refresh.
1778: discardOldCache = true;
1779: break;
1780: } else if (ssnode instanceof Element) {
1781: Element ssElement = (Element) ssnode;
1782: if (ssElement.getParentNode() == null) {
1783: // The node was probably removed from the document, refresh.
1784: discardOldCache = true;
1785: break;
1786: }
1787: // XXX TODO It seems there is always source document present,
1788: // even the rendered elements are computed here. (Investigate!).
1789: // else if (ssElement.getOwnerDocument() != document) {
1790: // // The node doesn't belong to this document, refresh.
1791: // discardOldCache = true;
1792: // break;
1793: // }
1794: }
1795: }
1796: }
1797: if (discardOldCache) {
1798: styleSheetNodes = null;
1799: }
1800:
1801: if (styleSheetNodes == null) {
1802: // <nb>
1803: // styleSheetNodes = new ArrayList();
1804: // ===
1805: styleSheetNodes = new ArrayList<WeakReference<CSSStyleSheetNode>>();
1806: // </nb>
1807: // <rave>
1808: if (document == null) {
1809: return styleSheetNodes;
1810: }
1811: // </rave>
1812: selectorAttributes = new HashSet();
1813: // Find all the style-sheets in the document.
1814: // <rave>
1815: // findSylesSheetNodes(document);
1816: findStyleSheetNodes();
1817: // </rave>
1818: int len = styleSheetNodes.size();
1819: for (int i = 0; i < len; i++) {
1820: CSSStyleSheetNode ssn;
1821: // <nb>
1822: // ssn = (CSSStyleSheetNode)styleSheetNodes.get(i);
1823: // ===
1824: WeakReference<CSSStyleSheetNode> ssnWRef = styleSheetNodes
1825: .get(i);
1826: ssn = ssnWRef == null ? null : ssnWRef.get();
1827: // </nb
1828: StyleSheet ss = ssn.getCSSStyleSheet();
1829: if (ss != null) {
1830: findSelectorAttributes(selectorAttributes, ss);
1831: }
1832: }
1833: }
1834: return styleSheetNodes;
1835: }
1836:
1837: // <rave>
1838: protected void findStyleSheetNodes() {
1839: findStyleSheetNodes(document);
1840: }
1841:
1842: /**
1843: * Try to create a minimal (as short as possible) style string
1844: * from this map, by using shortcuts when possible.
1845: * This will for example compress border-color-left/right/bottom/top
1846: * into border-color: one two three four, and so on.
1847: *
1848: * The default implementation just passes serializes the map directly.
1849: */
1850: protected String toMinimalStyleString(StyleMap map) {
1851: return map.toStyleString(this );
1852: }
1853:
1854: // </rave>
1855: /**
1856: * An auxiliary method for getStyleSheets().
1857: */
1858: protected void findStyleSheetNodes(Node n) {
1859: if (n instanceof CSSStyleSheetNode) {
1860: // <nb>
1861: // styleSheetNodes.add(n);
1862: // ===
1863: styleSheetNodes.add(new WeakReference<CSSStyleSheetNode>(
1864: (CSSStyleSheetNode) n));
1865: // </nb>
1866: }
1867: for (Node nd = n.getFirstChild(); nd != null; nd = nd
1868: .getNextSibling()) {
1869: findStyleSheetNodes(nd);
1870: }
1871: }
1872:
1873: /**
1874: * Finds the selector attributes in the given stylesheet.
1875: */
1876: protected void findSelectorAttributes(Set attrs, StyleSheet ss) {
1877: int len = ss.getSize();
1878: for (int i = 0; i < len; i++) {
1879: Rule r = ss.getRule(i);
1880: switch (r.getType()) {
1881: case StyleRule.TYPE:
1882: StyleRule style = (StyleRule) r;
1883: SelectorList sl = style.getSelectorList();
1884: int slen = sl.getLength();
1885: for (int j = 0; j < slen; j++) {
1886: ExtendedSelector s = (ExtendedSelector) sl.item(j);
1887: s.fillAttributeSet(attrs);
1888: }
1889: break;
1890:
1891: case MediaRule.TYPE:
1892: case ImportRule.TYPE:
1893: MediaRule mr = (MediaRule) r;
1894: if (mediaMatch(mr.getMediaList())) {
1895: findSelectorAttributes(attrs, mr);
1896: }
1897: break;
1898: }
1899: }
1900: }
1901:
1902: /**
1903: * Interface for people interesting in having 'primary' properties
1904: * set. Shorthand properties will be expanded "automatically".
1905: */
1906: public interface MainPropertyReceiver {
1907: /**
1908: * Called with a non-shorthand property name and it's value.
1909: */
1910: public void setMainProperty(String name, Value v,
1911: boolean important);
1912: };
1913:
1914: public void setMainProperties(CSSStylableElement elt,
1915: final MainPropertyReceiver dst, String pname, String value,
1916: boolean important) {
1917: try {
1918: element = elt;
1919: LexicalUnit lu = parser.parsePropertyValue(value);
1920: ShorthandManager.PropertyHandler ph = new ShorthandManager.PropertyHandler() {
1921: public void property(String pname, LexicalUnit lu,
1922: boolean important) {
1923: int idx = getPropertyIndex(pname);
1924: if (idx != -1) {
1925: ValueManager vm = valueManagers[idx];
1926: Value v = vm.createValue(lu, CSSEngine.this );
1927: dst.setMainProperty(pname, v, important);
1928: return;
1929: }
1930: idx = getShorthandIndex(pname);
1931: if (idx == -1)
1932: return; // Unknown property...
1933: // Shorthand value
1934: // <rave>
1935: // BEGIN RAVE MODIFICATIONS
1936: expandingShorthandProperty = idx;
1937: // END RAVE MODIFICATIONS
1938: // </rave>
1939: shorthandManagers[idx].setValues(CSSEngine.this ,
1940: this , lu, important);
1941: // <rave>
1942: // BEGIN RAVE MODIFICATIONS
1943: expandingShorthandProperty = -1;
1944: // END RAVE MODIFICATIONS
1945: // </rave>
1946: }
1947: };
1948: ph.property(pname, lu, important);
1949: } catch (Exception e) {
1950: String m = e.getMessage();
1951: if (m == null)
1952: m = "";
1953: String u = ((documentURI == null) ? "<unknown>"
1954: : documentURI.toString());
1955: String s = Messages.formatMessage(
1956: "property.syntax.error.at", new Object[] { u,
1957: pname, value, m });
1958: DOMException de = new DOMException(DOMException.SYNTAX_ERR,
1959: s);
1960: // <rave>
1961: // BEGIN RAVE MODIFICATIONS
1962: de.initCause(e);
1963: // END RAVE MODIFICATIONS
1964: // </rave>
1965: if (userAgent == null)
1966: throw de;
1967: userAgent.displayError(de);
1968: } finally {
1969: element = null;
1970: cssBaseURI = null;
1971: }
1972: }
1973:
1974: /**
1975: * Parses and creates a property value from elt.
1976: * @param elt The element property is from.
1977: * @param prop The property name.
1978: * @param value The property value.
1979: */
1980: public Value parsePropertyValue(CSSStylableElement elt,
1981: String prop, String value) {
1982: int idx = getPropertyIndex(prop);
1983: if (idx == -1)
1984: return null;
1985: ValueManager vm = valueManagers[idx];
1986: try {
1987: element = elt;
1988: LexicalUnit lu;
1989: lu = parser.parsePropertyValue(value);
1990: return vm.createValue(lu, this );
1991: // BEGIN RAVE MODIFICATIONS
1992: // XXX when is this used?
1993: // END RAVE MODIFICATIONS
1994: } catch (Exception e) {
1995: String m = e.getMessage();
1996: if (m == null)
1997: m = "";
1998: String u = ((documentURI == null) ? "<unknown>"
1999: : documentURI.toString());
2000: String s = Messages.formatMessage(
2001: "property.syntax.error.at", new Object[] { u, prop,
2002: value, m });
2003: DOMException de = new DOMException(DOMException.SYNTAX_ERR,
2004: s);
2005: // <rave>
2006: // BEGIN RAVE MODIFICATIONS
2007: de.initCause(e);
2008: // END RAVE MODIFICATIONS
2009: // </rave>
2010: if (userAgent == null)
2011: throw de;
2012: userAgent.displayError(de);
2013: } finally {
2014: element = null;
2015: cssBaseURI = null;
2016: }
2017: return vm.getDefaultValue();
2018: }
2019:
2020: /**
2021: * Parses and creates a style declaration.
2022: * @param value The style declaration text.
2023: */
2024: public StyleDeclaration parseStyleDeclaration(
2025: CSSStylableElement elt, String value) {
2026: styleDeclarationBuilder.styleDeclaration = new StyleDeclaration();
2027: try {
2028: element = elt;
2029: parser.setSelectorFactory(CSSSelectorFactory.INSTANCE);
2030: parser.setConditionFactory(cssConditionFactory);
2031: parser.setDocumentHandler(styleDeclarationBuilder);
2032: // <rave>
2033: // BEGIN RAVE MODIFICATIONS
2034: styleDeclarationBuilder.location = elt;
2035: styleDeclarationBuilder.lineno = -1;
2036: // END RAVE MODIFICATIONS
2037: // </rave>
2038: parser.parseStyleDeclaration(value);
2039: } catch (Exception e) {
2040: String m = e.getMessage();
2041: if (m == null)
2042: m = "";
2043: String u = ((documentURI == null) ? "<unknown>"
2044: : documentURI.toString());
2045: String s = Messages.formatMessage("syntax.error.at",
2046: new Object[] { u, m });
2047: DOMException de = new DOMException(DOMException.SYNTAX_ERR,
2048: s);
2049: // <rave>
2050: // BEGIN RAVE MODIFICATIONS
2051: de.initCause(e);
2052: // END RAVE MODIFICATIONS
2053: // </rave>
2054: if (userAgent == null)
2055: throw de;
2056: userAgent.displayError(de);
2057: } finally {
2058: element = null;
2059: cssBaseURI = null;
2060: }
2061: return styleDeclarationBuilder.styleDeclaration;
2062: }
2063:
2064: /**
2065: * Parses and creates a new style-sheet.
2066: * @param uri The style-sheet URI.
2067: * @param media The target media of the style-sheet.
2068: */
2069: // <rave>
2070: // BEGIN RAVE MODIFICATIONS
2071: // public StyleSheet parseStyleSheet(URL uri, String media)
2072: public StyleSheet parseStyleSheet(URL uri, String media,
2073: Object location)
2074: // END RAVE MODIFICATIONS
2075: // </rave>
2076: throws DOMException {
2077: // <rave>
2078: // BEGIN RAVE MODIFICATIONS
2079: //StyleSheet ss = new StyleSheet();
2080: StyleSheetCache cache = StyleSheetCache.getInstance();
2081: StyleSheet ss = cache.get(uri);
2082: if (ss == PARSING_SHEET) {
2083: warnCircularReference(uri, location);
2084: return new StyleSheet();
2085: } else if (ss != null) {
2086: return ss;
2087: }
2088: StyleSheet parsedSheet = null;
2089: ss = new StyleSheet();
2090: try {
2091: cache.put(uri, PARSING_SHEET);
2092: // END RAVE MODIFICATIONS
2093: // </rave>
2094: try {
2095: ss.setMedia(parser.parseMedia(media));
2096: } catch (Exception e) {
2097: String m = e.getMessage();
2098: if (m == null)
2099: m = "";
2100: String u = ((documentURI == null) ? "<unknown>"
2101: : documentURI.toString());
2102: String s = Messages.formatMessage("syntax.error.at",
2103: new Object[] { u, m });
2104: DOMException de = new DOMException(
2105: DOMException.SYNTAX_ERR, s);
2106: // <rave>
2107: // BEGIN RAVE MODIFICATIONS
2108: de.initCause(e);
2109: // END RAVE MODIFICATIONS
2110: // </rave>
2111: if (userAgent == null)
2112: throw de;
2113: userAgent.displayError(de);
2114: return ss;
2115: }
2116: // <rave>
2117: // BEGIN RAVE MODIFICATIONS
2118: // parseStyleSheet(ss, uri);
2119: parseStyleSheet(ss, uri, location);
2120: parsedSheet = ss;
2121: } finally {
2122: cache.put(uri, parsedSheet);
2123: }
2124: // END RAVE MODIFICATIONS
2125: // </rave>
2126: return ss;
2127: }
2128:
2129: /**
2130: * Parses and creates a new style-sheet.
2131: * @param is The input source used to read the document.
2132: * @param uri The base URI.
2133: * @param media The target media of the style-sheet.
2134: */
2135: // <rave>
2136: // BEGIN RAVE MODIFICATIONS
2137: // public StyleSheet parseStyleSheet(InputSource is, URL uri, String media)
2138: public StyleSheet parseStyleSheet(InputSource is, URL uri,
2139: String media, Object location)
2140: // END RAVE MODIFICATIONS
2141: // </rave>
2142: throws DOMException {
2143:
2144: // <rave>
2145: // BEGIN RAVE MODIFICATIONS
2146: //StyleSheet ss = new StyleSheet();
2147: StyleSheetCache cache = StyleSheetCache.getInstance();
2148: StyleSheet ss = cache.get(uri);
2149: if (ss == PARSING_SHEET) {
2150: warnCircularReference(uri, location);
2151: return new StyleSheet();
2152: } else if (ss != null) {
2153: return ss;
2154: }
2155: StyleSheet parsedSheet = null;
2156: ss = new StyleSheet();
2157: cache.put(uri, PARSING_SHEET);
2158: // END RAVE MODIFICATIONS
2159: // </rave>
2160: try {
2161: ss.setMedia(parser.parseMedia(media));
2162: // <rave>
2163: // BEGIN RAVE MODIFICATIONS
2164: // parseStyleSheet(ss, is, uri);
2165: parseStyleSheet(ss, is, uri, location);
2166: parsedSheet = ss;
2167: // END RAVE MODIFICATIONS
2168: // </rave>
2169: } catch (Exception e) {
2170: String m = e.getMessage();
2171: if (m == null)
2172: m = "";
2173: String u = ((documentURI == null) ? "<unknown>"
2174: : documentURI.toString());
2175: String s = Messages.formatMessage("syntax.error.at",
2176: new Object[] { u, m });
2177: DOMException de = new DOMException(DOMException.SYNTAX_ERR,
2178: s);
2179: // <rave>
2180: // BEGIN RAVE MODIFICATIONS
2181: de.initCause(e);
2182: // END RAVE MODIFICATIONS
2183: // </rave>
2184: if (userAgent == null)
2185: throw de;
2186: userAgent.displayError(de);
2187: // <rave>
2188: } finally {
2189: cache.put(uri, parsedSheet);
2190: // </rave>
2191: }
2192: return ss;
2193: }
2194:
2195: /**
2196: * Parses and fills the given style-sheet.
2197: * @param ss The stylesheet to fill.
2198: * @param uri The base URI.
2199: */
2200: // <rave>
2201: // BEGIN RAVE MODIFICATIONS
2202: // public void parseStyleSheet(StyleSheet ss, URL uri) throws DOMException {
2203: public void parseStyleSheet(StyleSheet ss, URL uri, Object location)
2204: throws DOMException {
2205: // END RAVE MODIFICATIONS
2206: // </rave>
2207: if (uri == null) {
2208: String s = Messages.formatMessage("syntax.error.at",
2209: new Object[] { "Null Document reference", "" });
2210: DOMException de = new DOMException(DOMException.SYNTAX_ERR,
2211: s);
2212: if (userAgent == null)
2213: throw de;
2214: userAgent.displayError(de);
2215: return;
2216: }
2217:
2218: try {
2219: // Check that access to the uri is allowed
2220: // <rave>
2221: // BEGIN RAVE MODIFICATIONS
2222: // We don't check external resources so no need to parse
2223: // and process url
2224: // ParsedURL pDocURL = null;
2225: // if (documentURI != null) {
2226: // pDocURL = new ParsedURL(documentURI);
2227: // }
2228: // ParsedURL pURL = new ParsedURL(uri);
2229: // cssContext.checkLoadExternalResource(pURL, pDocURL);
2230: // END RAVE MODIFICATIONS
2231: // </rave>
2232:
2233: // <rave>
2234: // BEGIN RAVE MODIFICATIONS
2235: // parseStyleSheet(ss, new InputSource(uri.toString()), uri);
2236: parseStyleSheet(ss, new InputSource(uri.toString()), uri,
2237: location);
2238: // END RAVE MODIFICATIONS
2239: // </rave>
2240: } catch (SecurityException e) {
2241: throw e;
2242: } catch (Exception e) {
2243: // <rave>
2244: if (e instanceof CSSException
2245: && ((CSSException) e).getException() instanceof java.io.IOException) {
2246: displayMissingStyleSheet(uri.toString());
2247: return;
2248: }
2249: // </rave>
2250: String m = e.getMessage();
2251: if (m == null)
2252: m = "";
2253: String s = Messages.formatMessage("syntax.error.at",
2254: new Object[] { uri.toString(), m });
2255: DOMException de = new DOMException(DOMException.SYNTAX_ERR,
2256: s);
2257: // <rave>
2258: // BEGIN RAVE MODIFICATIONS
2259: de.initCause(e);
2260: // END RAVE MODIFICATIONS
2261: // </rave>
2262: if (userAgent == null)
2263: throw de;
2264: userAgent.displayError(de);
2265: }
2266: }
2267:
2268: /**
2269: * Parses and creates a new style-sheet.
2270: * @param rules The style-sheet rules to parse.
2271: * @param uri The style-sheet URI.
2272: * @param media The target media of the style-sheet.
2273: */
2274: // <rave>
2275: // BEGIN RAVE MODIFICATIONS
2276: // public StyleSheet parseStyleSheet(String rules, URL uri, String media)
2277: public StyleSheet parseStyleSheet(String rules, URL uri,
2278: String media, Object location)
2279: // END RAVE MODIFICATIONS
2280: // </rave>
2281: throws DOMException {
2282: StyleSheet ss = new StyleSheet();
2283: // <rave>
2284: // BEGIN RAVE MODIFICATIONS
2285: // Can we cache by string values too?
2286: //StyleSheet ss = StyleSheetCache.getInstance().get(rules);
2287: //if (ss != null) {
2288: // return ss;
2289: //}
2290: //ss = new StyleSheet();
2291: // END RAVE MODIFICATIONS
2292: // </rave>
2293: try {
2294: ss.setMedia(parser.parseMedia(media));
2295: } catch (Exception e) {
2296: String m = e.getMessage();
2297: if (m == null)
2298: m = "";
2299: String u = ((documentURI == null) ? "<unknown>"
2300: : documentURI.toString());
2301: String s = Messages.formatMessage("syntax.error.at",
2302: new Object[] { u, m });
2303: DOMException de = new DOMException(DOMException.SYNTAX_ERR,
2304: s);
2305: // <rave>
2306: // BEGIN RAVE MODIFICATIONS
2307: de.initCause(e);
2308: // END RAVE MODIFICATIONS
2309: // </rave>
2310: if (userAgent == null)
2311: throw de;
2312: userAgent.displayError(de);
2313: return ss;
2314: }
2315: // <rave>
2316: // BEGIN RAVE MODIFICATIONS
2317: // parseStyleSheet(ss, rules, uri);
2318: parseStyleSheet(ss, rules, uri, location);
2319: //StyleSheetCache.getInstance().put(rules, ss);
2320: // END RAVE MODIFICATIONS
2321: // </rave>
2322: return ss;
2323: }
2324:
2325: /**
2326: * Parses and fills the given style-sheet.
2327: * @param ss The stylesheet to fill.
2328: * @param rules The style-sheet rules to parse.
2329: * @param uri The base URI.
2330: */
2331: public void parseStyleSheet(StyleSheet ss, String rules,
2332: // <rave>
2333: // BEGIN RAVE MODIFICATIONS
2334: // URL uri) throws DOMException {
2335: URL uri, Object location) throws DOMException {
2336: // END RAVE MODIFICATIONS
2337: // </rave>
2338: try {
2339: parseStyleSheet(ss,
2340: new InputSource(new StringReader(rules)), uri
2341: // BEGIN RAVE MODIFICATIONS
2342: , location
2343: // END RAVE MODIFICATIONS
2344: );
2345: } catch (Exception e) {
2346: String m = e.getMessage();
2347: if (m == null)
2348: m = "";
2349:
2350: // <rave>
2351: // String s = Messages.formatMessage
2352: // ("stylesheet.syntax.error",
2353: // new Object[] { uri.toString(), rules, m });
2354: String s = "";
2355: if (uri == null) {
2356: s = Messages.formatMessage("stylesheet.syntax.error",
2357: new Object[] { "None", rules, m });
2358: } else {
2359: s = Messages.formatMessage("stylesheet.syntax.error",
2360: new Object[] { uri.toString(), rules, m });
2361: }
2362: // </rave>
2363: DOMException de = new DOMException(DOMException.SYNTAX_ERR,
2364: s);
2365: // <rave>
2366: // BEGIN RAVE MODIFICATIONS
2367: de.initCause(e);
2368: // END RAVE MODIFICATIONS
2369: // </rave>
2370: if (userAgent == null)
2371: throw de;
2372: userAgent.displayError(de);
2373: }
2374: }
2375:
2376: /**
2377: * Parses and fills the given style-sheet.
2378: * @param ss The stylesheet to fill.
2379: * @param uri The base URI.
2380: */
2381: // <rave>
2382: // BEGIN RAVE MODIFICATIONS
2383: // protected void parseStyleSheet(StyleSheet ss, InputSource is, URL uri)
2384: protected void parseStyleSheet(StyleSheet ss, InputSource is,
2385: URL uri, Object location)
2386: // END RAVE MODIFICATIONS
2387: // </rave>
2388: throws IOException {
2389: parser.setSelectorFactory(CSSSelectorFactory.INSTANCE);
2390: parser.setConditionFactory(cssConditionFactory);
2391: try {
2392: cssBaseURI = uri;
2393: styleSheetDocumentHandler.styleSheet = ss;
2394: parser.setDocumentHandler(styleSheetDocumentHandler);
2395: // BEGIN RAVE MODIFICATIONS
2396: // Set up some context. This will allow values to track their
2397: // source file and line number
2398: //styleSheetDocumentHandler.location = uri;
2399: styleSheetDocumentHandler.location = location;
2400: styleSheetDocumentHandler.lineno = 0;
2401: // END RAVE MODIFICATIONS
2402: parser.parseStyleSheet(is);
2403:
2404: // Load the imported sheets.
2405: int len = ss.getSize();
2406: for (int i = 0; i < len; i++) {
2407: Rule r = ss.getRule(i);
2408: if (r.getType() != ImportRule.TYPE) {
2409: // @import rules must be the first rules.
2410: break;
2411: }
2412: ImportRule ir = (ImportRule) r;
2413: // <rave>
2414: // BEGIN RAVE MODIFICATIONS
2415: StyleSheetCache cache = StyleSheetCache.getInstance();
2416: StyleSheet sheet = cache.get(ir.getURI());
2417: if (sheet == PARSING_SHEET) {
2418: warnCircularReference(ir.getURI(), location);
2419: continue;
2420: }
2421: // TODO - just populate the cached styles from
2422: // sheet into this stylesheet? (ss)
2423: // parseStyleSheet(ir, ir.getURI());
2424: parseStyleSheet(ir, ir.getURI(), ir.getURI());
2425: // END RAVE MODIFICATIONS
2426: // </rave>
2427: }
2428: } finally {
2429: cssBaseURI = null;
2430: }
2431: }
2432:
2433: /**
2434: * Puts an author property from a style-map in another style-map,
2435: * if possible.
2436: */
2437: protected void putAuthorProperty(StyleMap dest, int idx,
2438: Value sval, boolean imp, short origin) {
2439: Value dval = dest.getValue(idx);
2440: short dorg = dest.getOrigin(idx);
2441: boolean dimp = dest.isImportant(idx);
2442:
2443: boolean cond = dval == null;
2444: if (!cond) {
2445: switch (dorg) {
2446: case StyleMap.USER_ORIGIN:
2447: cond = !dimp;
2448: break;
2449: case StyleMap.AUTHOR_ORIGIN:
2450: cond = !dimp || imp;
2451: break;
2452: default:
2453: cond = true;
2454: }
2455: }
2456:
2457: if (cond) {
2458: dest.putValue(idx, sval);
2459: dest.putImportant(idx, imp);
2460: dest.putOrigin(idx, origin);
2461: }
2462: }
2463:
2464: // <rave>
2465: // BEGIN RAVE MODIFICATIONS
2466: private void addCandidateRules(List rules, Map map, String key,
2467: Element elt, String pseudo) {
2468: ExtendedSelector[] rs = (ExtendedSelector[]) map.get(key);
2469: if (rs != null) {
2470: for (int i = 0; i < rs.length; i++) {
2471: ExtendedSelector s = rs[i];
2472: if (s.match(elt, pseudo)) {
2473: Rule r = s.getRule();
2474: assert r.getType() == StyleRule.TYPE;
2475: rules.add(r);
2476: }
2477: }
2478: }
2479: }
2480:
2481: private Comparator ruleComparator = new Comparator() {
2482: public int compare(Object object1, Object object2) {
2483: Rule rule1 = (Rule) object1;
2484: Rule rule2 = (Rule) object2;
2485: return rule1.getPosition() - rule2.getPosition();
2486: }
2487: };
2488:
2489: // END RAVE MODIFICATIONS
2490: // </rave>
2491: /**
2492: * Adds the rules matching the element/pseudo-element of given style
2493: * sheet to the list.
2494: */
2495: protected void addMatchingRules(List rules, StyleSheet ss,
2496: Element elt, String pseudo) {
2497: // <rave>
2498: // BEGIN RAVE MODIFICATIONS
2499: // addMatchingRules was the hottest method in a full page refresh.
2500: // On a Braveheart page, it's extremely long. The reason for that
2501: // of course is that there's a huge number of rules in the stylesheet -
2502: // over 700, where EACH has a number of selectors, and each selector
2503: // may even do node iteration, and these are then checked for every
2504: // document element! For complicated html pages like MyYahoo, it
2505: // spent 40 seconds in rule matching for the dom elements!
2506: // This clearly requires an optimization. I'm using one described by
2507: // David Hyatt in a blog, where the rule set is partitioned into
2508: // rules that can be quickly checked based on the tag name, the class
2509: // attribute, and the element id. Rules that don't fit in these buckets
2510: // are then checked in the normal, linear way. For the tag/class/id
2511: // rules, they are stored in hashmaps keyed by the tagname/class/id,
2512: // so for a given element we can quickly look up all rules that affect
2513: // that tag - and more importantly, ignore rules that pertain only
2514: // to OTHER tags!
2515: // This yields a huge performance benefit -- 7x-8x in my measurements.
2516: // There are some complications to worry about, such as needing to
2517: // sort the rules according to their stylesheet order, to preserve the
2518: // right cascade semantics. These are handled below.
2519: // Note that the stylesheet initialization code, which sets up these
2520: // data structures, is run only once. I've deliberately done more
2521: // computation there which should help speed up this hot method; e.g.
2522: // the data structures are typed arrays rather than generic Object
2523: // collections, etc.
2524: if (RULE_FILTERING) {
2525: List newRules = new ArrayList(ss.getSize());
2526:
2527: Map tagMap = ss.getTagMap();
2528: if (tagMap != null) {
2529: String tagName = elt.getTagName();
2530: addCandidateRules(newRules, tagMap, tagName, elt,
2531: pseudo);
2532: }
2533:
2534: Map classMap = ss.getClassMap();
2535: if (classMap != null) {
2536: String styleClass = elt.getAttribute("class");
2537: if (styleClass.length() == 0) {
2538: } else if (styleClass.indexOf(' ') == -1) {
2539: addCandidateRules(newRules, classMap, styleClass,
2540: elt, pseudo);
2541: } else {
2542: int begin = 0;
2543: int length = styleClass.length();
2544: while (begin < length) {
2545: while (begin < length
2546: && Character.isSpace(styleClass
2547: .charAt(begin))) {
2548: begin++;
2549: }
2550: int end = begin + 1;
2551: while (end < length
2552: && !Character.isSpace(styleClass
2553: .charAt(end))) {
2554: end++;
2555: }
2556: if (begin < length && end > begin) {
2557: addCandidateRules(newRules, classMap,
2558: styleClass.substring(begin, end),
2559: elt, pseudo);
2560: }
2561: begin = end + 1;
2562: }
2563: }
2564: }
2565:
2566: Map idMap = ss.getIdMap();
2567: if (idMap != null) {
2568: String id = elt.getAttribute("id");
2569: if (id.length() > 0) {
2570: addCandidateRules(newRules, idMap, id, elt, pseudo);
2571: }
2572: }
2573:
2574: Rule[] rs = ss.getRemainingRules();
2575: if (rs != null) {
2576: for (int i = 0; i < rs.length; i++) {
2577: Rule r = rs[i];
2578: switch (r.getType()) {
2579: case StyleRule.TYPE:
2580: StyleRule style = (StyleRule) r;
2581: SelectorList sl = style.getSelectorList();
2582: int slen = sl.getLength();
2583: for (int j = 0; j < slen; j++) {
2584: ExtendedSelector s = (ExtendedSelector) sl
2585: .item(j);
2586: if (s.match(elt, pseudo)) {
2587: newRules.add(style);
2588: }
2589: }
2590: break;
2591:
2592: case MediaRule.TYPE:
2593: case ImportRule.TYPE:
2594: MediaRule mr = (MediaRule) r;
2595: if (mediaMatch(mr.getMediaList())) {
2596: addMatchingRules(newRules, mr, elt, pseudo);
2597: }
2598: break;
2599: }
2600: }
2601:
2602: // Sort the rules by order, since we've split up the order when
2603: // separating out tag name, attributes, etc.
2604: int size = newRules.size();
2605: if (size > 0) {
2606: if (size > 1) {
2607: Collections.sort(newRules, ruleComparator);
2608: }
2609:
2610: Object prev = null;
2611: Iterator it = newRules.iterator();
2612: while (it.hasNext()) {
2613: Object o = it.next();
2614: if (o != prev) {
2615: rules.add(o);
2616: prev = o;
2617: }
2618: }
2619: }
2620:
2621: return;
2622: }
2623: }
2624: // END RAVE MODIFICATIONS
2625: // </rave>
2626: int len = ss.getSize();
2627: for (int i = 0; i < len; i++) {
2628: Rule r = ss.getRule(i);
2629: switch (r.getType()) {
2630: case StyleRule.TYPE:
2631: StyleRule style = (StyleRule) r;
2632: SelectorList sl = style.getSelectorList();
2633: int slen = sl.getLength();
2634: for (int j = 0; j < slen; j++) {
2635: ExtendedSelector s = (ExtendedSelector) sl.item(j);
2636: if (s.match(elt, pseudo)) {
2637: rules.add(style);
2638: }
2639: }
2640: break;
2641:
2642: case MediaRule.TYPE:
2643: case ImportRule.TYPE:
2644: MediaRule mr = (MediaRule) r;
2645: if (mediaMatch(mr.getMediaList())) {
2646: addMatchingRules(rules, mr, elt, pseudo);
2647: }
2648: break;
2649: }
2650: }
2651: }
2652:
2653: /**
2654: * Adds the rules contained in the given list to a stylemap.
2655: */
2656: protected void addRules(Element elt, String pseudo, StyleMap sm,
2657: List rules, short origin) {
2658: sortRules(rules, elt, pseudo);
2659: int rlen = rules.size();
2660:
2661: if (origin == StyleMap.AUTHOR_ORIGIN) {
2662: for (int r = 0; r < rlen; r++) {
2663: StyleRule sr = (StyleRule) rules.get(r);
2664: StyleDeclaration sd = sr.getStyleDeclaration();
2665: int len = sd.size();
2666: for (int i = 0; i < len; i++) {
2667: putAuthorProperty(sm, sd.getIndex(i), sd
2668: .getValue(i), sd.getPriority(i), origin);
2669: }
2670: }
2671: } else {
2672: for (int r = 0; r < rlen; r++) {
2673: StyleRule sr = (StyleRule) rules.get(r);
2674: StyleDeclaration sd = sr.getStyleDeclaration();
2675: int len = sd.size();
2676: for (int i = 0; i < len; i++) {
2677: int idx = sd.getIndex(i);
2678: sm.putValue(idx, sd.getValue(i));
2679: sm.putImportant(idx, sd.getPriority(i));
2680: sm.putOrigin(idx, origin);
2681: }
2682: }
2683: }
2684: }
2685:
2686: /**
2687: * Sorts the rules matching the element/pseudo-element of given style
2688: * sheet to the list.
2689: */
2690: protected void sortRules(List rules, Element elt, String pseudo) {
2691: int len = rules.size();
2692: for (int i = 0; i < len - 1; i++) {
2693: int idx = i;
2694: int min = Integer.MAX_VALUE;
2695: for (int j = i; j < len; j++) {
2696: StyleRule r = (StyleRule) rules.get(j);
2697: SelectorList sl = r.getSelectorList();
2698: int spec = 0;
2699: int slen = sl.getLength();
2700: for (int k = 0; k < slen; k++) {
2701: ExtendedSelector s = (ExtendedSelector) sl.item(k);
2702: if (s.match(elt, pseudo)) {
2703: int sp = s.getSpecificity();
2704: if (sp > spec) {
2705: spec = sp;
2706: }
2707: }
2708: }
2709: if (spec < min) {
2710: min = spec;
2711: idx = j;
2712: }
2713: }
2714: if (i != idx) {
2715: Object tmp = rules.get(i);
2716: rules.set(i, rules.get(idx));
2717: rules.set(idx, tmp);
2718: }
2719: }
2720: }
2721:
2722: /**
2723: * Whether the given media list matches the media list of this
2724: * CSSEngine object.
2725: */
2726: protected boolean mediaMatch(SACMediaList ml) {
2727: if (media == null || ml == null || media.getLength() == 0
2728: || ml.getLength() == 0) {
2729: return true;
2730: }
2731: for (int i = 0; i < ml.getLength(); i++) {
2732: if (ml.item(i).equalsIgnoreCase("all"))
2733: return true;
2734: for (int j = 0; j < media.getLength(); j++) {
2735: if (media.item(j).equalsIgnoreCase("all")
2736: || ml.item(i).equalsIgnoreCase(media.item(j))) {
2737: return true;
2738: }
2739: }
2740: }
2741: return false;
2742: }
2743:
2744: /**
2745: * To parse a style declaration.
2746: */
2747: protected class StyleDeclarationDocumentHandler extends
2748: DocumentAdapter implements ShorthandManager.PropertyHandler {
2749: public StyleMap styleMap;
2750:
2751: // <rave>
2752: // BEGIN RAVE MODIFICATIONS
2753: Object location;
2754: int lineno;
2755:
2756: // END RAVE MOFIFICATIONS
2757: // </rave>
2758: /**
2759: * <b>SAC</b>: Implements {@link
2760: * DocumentHandler#property(String,LexicalUnit,boolean)}.
2761: */
2762: public void property(String name, LexicalUnit value,
2763: boolean important) throws CSSException {
2764: int i = getPropertyIndex(name);
2765: if (i == -1) {
2766: i = getShorthandIndex(name);
2767: if (i == -1) {
2768: // Unknown property
2769: // <rave>
2770: // BEGIN RAVE MODIFICATIONS
2771: if (unknownPropertyNames != null) {
2772: unknownPropertyNames.add(name);
2773: StringBuffer sb = new StringBuffer(50);
2774: appendCss(sb, value);
2775: unknownPropertyValues.add(sb.toString());
2776: }
2777: // END RAVE MODIFICATIONS
2778: // </rave>
2779: return;
2780: }
2781: // <rave>
2782: // BEGIN RAVE MODIFICATIONS
2783: try {
2784: expandingShorthandProperty = i;
2785: // END RAVE MODIFICATIONS
2786: // </rave>
2787: shorthandManagers[i].setValues(CSSEngine.this ,
2788: this , value, important);
2789: // <rave>
2790: // BEGIN RAVE MODIFICATIONS
2791: expandingShorthandProperty = -1;
2792: } catch (DOMException e) {
2793: // Something bad happened
2794: displayError(e, location, lineno + parser.getLine()
2795: - 1, parser.getColumn());
2796: // Continue processing - we're supposed to ignore
2797: // errant declarations!!!
2798: }
2799: // END RAVE MODIFICATIONS
2800: // </rave>
2801: } else {
2802: // <rave>
2803: // BEGIN RAVE MODIFICATIONS
2804: // Add error handling: don't abort, and send errors to
2805: // an output window instead via the engine
2806: try {
2807: // END RAVE MODIFICATIONS
2808: // </rave>
2809:
2810: Value v = valueManagers[i].createValue(value,
2811: CSSEngine.this );
2812: // <rave>
2813: // BEGIN RAVE MODIFICATIONS
2814: // Track the value source
2815: if (v instanceof AbstractValue) { // Should I add to Value interface???
2816: AbstractValue av = (AbstractValue) v;
2817: av.setLocation(location);
2818: av.setLineNumber(lineno + parser.getLine() - 1);
2819: }
2820: // END RAVE MODIFICATIONS
2821: // </rave>
2822: putAuthorProperty(styleMap, i, v, important,
2823: StyleMap.INLINE_AUTHOR_ORIGIN);
2824: // <rave>
2825: // BEGIN RAVE MODIFICATIONS
2826: } catch (DOMException e) {
2827: // Something bad happened
2828: displayError(e, location, lineno + parser.getLine()
2829: - 1, parser.getColumn());
2830: // Continue processing - we're supposed to ignore
2831: // errant declarations!!!
2832: }
2833: // END RAVE MODIFICATIONS
2834: // </rave>
2835: }
2836: }
2837: }
2838:
2839: /**
2840: * To build a StyleDeclaration object.
2841: */
2842: protected class StyleDeclarationBuilder extends DocumentAdapter
2843: implements ShorthandManager.PropertyHandler {
2844: public StyleDeclaration styleDeclaration;
2845: // <rave>
2846: // BEGIN RAVE MODIFICATIONS
2847: Object location;
2848: int lineno;
2849:
2850: // ENBD RAVE MODIFICATIONS
2851: // </rave>
2852: /**
2853: * <b>SAC</b>: Implements {@link
2854: * DocumentHandler#property(String,LexicalUnit,boolean)}.
2855: */
2856: public void property(String name, LexicalUnit value,
2857: boolean important) throws CSSException {
2858: int i = getPropertyIndex(name);
2859: if (i == -1) {
2860: i = getShorthandIndex(name);
2861: if (i == -1) {
2862: // Unknown property
2863: // <rave>
2864: // BEGIN RAVE MODIFICATIONS
2865: if (unknownPropertyNames != null) {
2866: unknownPropertyNames.add(name);
2867: StringBuffer sb = new StringBuffer(50);
2868: appendCss(sb, value);
2869: unknownPropertyValues.add(sb.toString());
2870: }
2871: // END RAVE MODIFICATIONS
2872: // </rave>
2873: return;
2874: }
2875: // <rave>
2876: // BEGIN RAVE MODIFICATIONS
2877: try {
2878: expandingShorthandProperty = i;
2879: // END RAVE MODIFICATIONS
2880: // </rave>
2881: shorthandManagers[i].setValues(CSSEngine.this ,
2882: this , value, important);
2883: // <rave>
2884: // BEGIN RAVE MODIFICATIONS
2885: expandingShorthandProperty = -1;
2886: } catch (DOMException e) {
2887: // Something bad happened
2888: displayError(e, location, lineno + parser.getLine()
2889: - 1, parser.getColumn());
2890: // Continue processing - we're supposed to ignore
2891: // errant declarations!!!
2892: }
2893: // END RAVE MODIFICATIONS
2894: // </rave>
2895: } else {
2896: // <rave>
2897: // BEGIN RAVE MODIFICATIONS
2898: // Add error handling: don't abort, and send errors to
2899: // an output window instead via the engine
2900: try {
2901: // END RAVE MODIFICATIONS
2902: // </rave>
2903:
2904: Value v = valueManagers[i].createValue(value,
2905: CSSEngine.this );
2906: // <rave>
2907: // BEGIN RAVE MODIFICATIONS
2908: // Track the value source
2909: if (v instanceof AbstractValue) { // Should I add to Value interface???
2910: AbstractValue av = (AbstractValue) v;
2911: av.setLocation(location);
2912: av.setLineNumber(lineno + parser.getLine() - 1);
2913: }
2914: // END RAVE MODIFICATIONS
2915: // </rave>
2916: styleDeclaration.append(v, i, important);
2917: // <rave>
2918: // BEGIN RAVE MODIFICATIONS
2919: } catch (DOMException e) {
2920: // Something bad happened
2921: displayError(e, location, lineno + parser.getLine()
2922: - 1, parser.getColumn());
2923: // Continue processing - we're supposed to ignore
2924: // errant declarations!!!
2925: }
2926: // END RAVE MODIFICATIONS
2927: // </rave>
2928: }
2929: }
2930: }
2931:
2932: /**
2933: * To parse a style sheet.
2934: */
2935: protected class StyleSheetDocumentHandler extends DocumentAdapter
2936: implements ShorthandManager.PropertyHandler {
2937: public StyleSheet styleSheet;
2938: // <rave>
2939: // BEGIN RAVE MODIFICATIONS
2940: Object location;
2941: int lineno;
2942: // END RAVE MODIFICATIONS
2943: // </rave>
2944: protected StyleRule styleRule;
2945: protected StyleDeclaration styleDeclaration;
2946:
2947: /**
2948: * <b>SAC</b>: Implements {@link
2949: * DocumentHandler#startDocument(InputSource)}.
2950: */
2951: public void startDocument(InputSource source)
2952: throws CSSException {
2953: }
2954:
2955: /**
2956: * <b>SAC</b>: Implements {@link
2957: * DocumentHandler#endDocument(InputSource)}.
2958: */
2959: public void endDocument(InputSource source) throws CSSException {
2960: }
2961:
2962: /**
2963: * <b>SAC</b>: Implements {@link
2964: * org.w3c.css.sac.DocumentHandler#ignorableAtRule(String)}.
2965: */
2966: public void ignorableAtRule(String atRule) throws CSSException {
2967: }
2968:
2969: /**
2970: * <b>SAC</b>: Implements {@link
2971: * DocumentHandler#importStyle(String,SACMediaList,String)}.
2972: */
2973: public void importStyle(String uri, SACMediaList media,
2974: String defaultNamespaceURI) throws CSSException {
2975: ImportRule ir = new ImportRule();
2976: // <rave>
2977: // BEGIN RAVE MODIFICATIONS
2978: ir.setRelativeUri(uri);
2979: // END RAVE MODIFICATIONS
2980: // </rave>
2981: ir.setMediaList(media);
2982: ir.setParent(styleSheet);
2983: try {
2984: URL base = getCSSBaseURI();
2985: URL url;
2986: if (base == null)
2987: url = new URL(uri);
2988: else
2989: url = new URL(base, uri);
2990: ir.setURI(url);
2991: } catch (MalformedURLException e) {
2992: }
2993: styleSheet.append(ir);
2994: }
2995:
2996: /**
2997: * <b>SAC</b>: Implements {@link
2998: * org.w3c.css.sac.DocumentHandler#startMedia(SACMediaList)}.
2999: */
3000: public void startMedia(SACMediaList media) throws CSSException {
3001: MediaRule mr = new MediaRule();
3002: mr.setMediaList(media);
3003: mr.setParent(styleSheet);
3004: styleSheet.append(mr);
3005: styleSheet = mr;
3006: }
3007:
3008: /**
3009: * <b>SAC</b>: Implements {@link
3010: * org.w3c.css.sac.DocumentHandler#endMedia(SACMediaList)}.
3011: */
3012: public void endMedia(SACMediaList media) throws CSSException {
3013: styleSheet = styleSheet.getParent();
3014: }
3015:
3016: /**
3017: * <b>SAC</b>: Implements {@link
3018: * org.w3c.css.sac.DocumentHandler#startPage(String,String)}.
3019: */
3020: public void startPage(String name, String pseudo_page)
3021: throws CSSException {
3022: }
3023:
3024: /**
3025: * <b>SAC</b>: Implements {@link
3026: * org.w3c.css.sac.DocumentHandler#endPage(String,String)}.
3027: */
3028: public void endPage(String name, String pseudo_page)
3029: throws CSSException {
3030: }
3031:
3032: /**
3033: * <b>SAC</b>: Implements {@link
3034: * org.w3c.css.sac.DocumentHandler#startFontFace()}.
3035: */
3036: public void startFontFace() throws CSSException {
3037: styleDeclaration = new StyleDeclaration();
3038: }
3039:
3040: /**
3041: * <b>SAC</b>: Implements {@link
3042: * org.w3c.css.sac.DocumentHandler#endFontFace()}.
3043: */
3044: public void endFontFace() throws CSSException {
3045: // <rave>
3046: // BEGIN RAVE MODIFICATIONS
3047: // Add error handling: don't abort, and send errors to
3048: // an output window instead via the engine
3049: try {
3050: // END RAVE MODIFICATIONS
3051: // </rave>
3052:
3053: StyleMap sm = new StyleMap(getNumberOfProperties());
3054: int len = styleDeclaration.size();
3055: for (int i = 0; i < len; i++) {
3056: int idx = styleDeclaration.getIndex(i);
3057: sm.putValue(idx, styleDeclaration.getValue(i));
3058: sm.putImportant(idx, styleDeclaration
3059: .getPriority(i));
3060: // Not sure on this..
3061: sm.putOrigin(idx, StyleMap.AUTHOR_ORIGIN);
3062: }
3063: styleDeclaration = null;
3064:
3065: int pidx = getPropertyIndex(CSSConstants.CSS_FONT_FAMILY_PROPERTY);
3066: Value fontFamily = sm.getValue(pidx);
3067: if (fontFamily == null)
3068: return;
3069:
3070: URL base = getCSSBaseURI();
3071: ParsedURL purl = null;
3072: if (base != null)
3073: purl = new ParsedURL(base);
3074: fontFaces.add(new FontFaceRule(sm, purl));
3075: // <rave>
3076: // BEGIN RAVE MODIFICATIONS
3077: } catch (DOMException e) {
3078: // Something bad happened
3079: //displayError(e, location, lineno);
3080: displayError(e, location,
3081: lineno + parser.getLine() - 1, parser
3082: .getColumn());
3083:
3084: // Continue processing - we're supposed to ignore
3085: // errant declarations!!!
3086: }
3087: // END RAVE MODIFICATIONS
3088: // </rave>
3089: }
3090:
3091: /**
3092: * <b>SAC</b>: Implements {@link
3093: * org.w3c.css.sac.DocumentHandler#startSelector(SelectorList)}.
3094: */
3095: public void startSelector(SelectorList selectors)
3096: throws CSSException {
3097: styleRule = new StyleRule();
3098: styleRule.setSelectorList(selectors);
3099: styleDeclaration = new StyleDeclaration();
3100: styleRule.setStyleDeclaration(styleDeclaration);
3101: styleSheet.append(styleRule);
3102: }
3103:
3104: /**
3105: * <b>SAC</b>: Implements {@link
3106: * org.w3c.css.sac.DocumentHandler#endSelector(SelectorList)}.
3107: */
3108: public void endSelector(SelectorList selectors)
3109: throws CSSException {
3110: styleRule = null;
3111: styleDeclaration = null;
3112: }
3113:
3114: /**
3115: * <b>SAC</b>: Implements {@link
3116: * DocumentHandler#property(String,LexicalUnit,boolean)}.
3117: */
3118: public void property(String name, LexicalUnit value,
3119: boolean important) throws CSSException {
3120: int i = getPropertyIndex(name);
3121: if (i == -1) {
3122: i = getShorthandIndex(name);
3123: if (i == -1) {
3124: // Unknown property
3125: return;
3126: }
3127: // <rave>
3128: // BEGIN RAVE MODIFICATIONS
3129: try {
3130: expandingShorthandProperty = i;
3131: // END RAVE MODIFICATIONS
3132: // </rave>
3133: shorthandManagers[i].setValues(CSSEngine.this ,
3134: this , value, important);
3135: // <rave>
3136: // BEGIN RAVE MODIFICATIONS
3137: expandingShorthandProperty = -1;
3138: } catch (DOMException e) {
3139: // Something bad happened
3140: displayError(e, location, lineno + parser.getLine()
3141: - 1, parser.getColumn());
3142: // Continue processing - we're supposed to ignore
3143: // errant declarations!!!
3144: }
3145: // END RAVE MODIFICATIONS
3146: // </rave>
3147: } else {
3148: // <rave>
3149: // BEGIN RAVE MODIFICATIONS
3150: // Add error handling: don't abort, and send errors to
3151: // an output window instead via the engine
3152: try {
3153: // END RAVE MODIFICATIONS
3154: // </rave>
3155:
3156: Value v = valueManagers[i].createValue(value,
3157: CSSEngine.this );
3158: // <rave>
3159: // BEGIN RAVE MODIFICATIONS
3160: // Track the value source
3161: if (v instanceof AbstractValue) { // Should I add to Value interface???
3162: AbstractValue av = (AbstractValue) v;
3163: av.setLocation(location);
3164: av.setLineNumber(lineno + parser.getLine() - 1);
3165: }
3166: // END RAVE MODIFICATIONS
3167: // </rave>
3168: styleDeclaration.append(v, i, important);
3169:
3170: // <rave>
3171: // BEGIN RAVE MODIFICATIONS
3172: } catch (DOMException e) {
3173: // Something bad happened
3174: displayError(e, location, lineno + parser.getLine()
3175: - 1, parser.getColumn());
3176: // Continue processing - we're supposed to ignore
3177: // errant declarations!!!
3178: }
3179: // END RAVE MODIFICATIONS
3180: // </rave>
3181: }
3182: }
3183: }
3184:
3185: /**
3186: * Provides an adapter for the DocumentHandler interface.
3187: */
3188: protected static class DocumentAdapter implements DocumentHandler {
3189:
3190: /**
3191: * <b>SAC</b>: Implements {@link
3192: * DocumentHandler#startDocument(InputSource)}.
3193: */
3194: public void startDocument(InputSource source)
3195: throws CSSException {
3196: throw new InternalError();
3197: }
3198:
3199: /**
3200: * <b>SAC</b>: Implements {@link
3201: * DocumentHandler#endDocument(InputSource)}.
3202: */
3203: public void endDocument(InputSource source) throws CSSException {
3204: throw new InternalError();
3205: }
3206:
3207: /**
3208: * <b>SAC</b>: Implements {@link
3209: * DocumentHandler#comment(String)}.
3210: */
3211: public void comment(String text) throws CSSException {
3212: // We always ignore the comments.
3213: }
3214:
3215: /**
3216: * <b>SAC</b>: Implements {@link
3217: * DocumentHandler#ignorableAtRule(String)}.
3218: */
3219: public void ignorableAtRule(String atRule) throws CSSException {
3220: throw new InternalError();
3221: }
3222:
3223: /**
3224: * <b>SAC</b>: Implements {@link
3225: * DocumentHandler#namespaceDeclaration(String,String)}.
3226: */
3227: public void namespaceDeclaration(String prefix, String uri)
3228: throws CSSException {
3229: throw new InternalError();
3230: }
3231:
3232: /**
3233: * <b>SAC</b>: Implements {@link
3234: * DocumentHandler#importStyle(String,SACMediaList,String)}.
3235: */
3236: public void importStyle(String uri, SACMediaList media,
3237: String defaultNamespaceURI) throws CSSException {
3238: throw new InternalError();
3239: }
3240:
3241: /**
3242: * <b>SAC</b>: Implements {@link
3243: * DocumentHandler#startMedia(SACMediaList)}.
3244: */
3245: public void startMedia(SACMediaList media) throws CSSException {
3246: throw new InternalError();
3247: }
3248:
3249: /**
3250: * <b>SAC</b>: Implements {@link
3251: * DocumentHandler#endMedia(SACMediaList)}.
3252: */
3253: public void endMedia(SACMediaList media) throws CSSException {
3254: throw new InternalError();
3255: }
3256:
3257: /**
3258: * <b>SAC</b>: Implements {@link
3259: * DocumentHandler#startPage(String,String)}.
3260: */
3261: public void startPage(String name, String pseudo_page)
3262: throws CSSException {
3263: throw new InternalError();
3264: }
3265:
3266: /**
3267: * <b>SAC</b>: Implements {@link
3268: * DocumentHandler#endPage(String,String)}.
3269: */
3270: public void endPage(String name, String pseudo_page)
3271: throws CSSException {
3272: throw new InternalError();
3273: }
3274:
3275: /**
3276: * <b>SAC</b>: Implements {@link DocumentHandler#startFontFace()}.
3277: */
3278: public void startFontFace() throws CSSException {
3279: throw new InternalError();
3280: }
3281:
3282: /**
3283: * <b>SAC</b>: Implements {@link DocumentHandler#endFontFace()}.
3284: */
3285: public void endFontFace() throws CSSException {
3286: throw new InternalError();
3287: }
3288:
3289: /**
3290: * <b>SAC</b>: Implements {@link
3291: * DocumentHandler#startSelector(SelectorList)}.
3292: */
3293: public void startSelector(SelectorList selectors)
3294: throws CSSException {
3295: throw new InternalError();
3296: }
3297:
3298: /**
3299: * <b>SAC</b>: Implements {@link
3300: * DocumentHandler#endSelector(SelectorList)}.
3301: */
3302: public void endSelector(SelectorList selectors)
3303: throws CSSException {
3304: throw new InternalError();
3305: }
3306:
3307: /**
3308: * <b>SAC</b>: Implements {@link
3309: * DocumentHandler#property(String,LexicalUnit,boolean)}.
3310: */
3311: public void property(String name, LexicalUnit value,
3312: boolean important) throws CSSException {
3313: throw new InternalError();
3314: }
3315: }
3316:
3317: // CSS events /////////////////////////////////////////////////////////
3318:
3319: protected final static CSSEngineListener[] LISTENER_ARRAY = new CSSEngineListener[0];
3320:
3321: /**
3322: * Adds a CSS engine listener.
3323: */
3324: public void addCSSEngineListener(CSSEngineListener l) {
3325: listeners.add(l);
3326: }
3327:
3328: /**
3329: * Removes a CSS engine listener.
3330: */
3331: public void removeCSSEngineListener(CSSEngineListener l) {
3332: listeners.remove(l);
3333: }
3334:
3335: /**
3336: * Fires a CSSEngineEvent, given a list of modified properties.
3337: */
3338: protected void firePropertiesChangedEvent(Element target,
3339: int[] props) {
3340: CSSEngineListener[] ll = (CSSEngineListener[]) listeners
3341: .toArray(LISTENER_ARRAY);
3342:
3343: int len = ll.length;
3344: if (len > 0) {
3345: CSSEngineEvent evt = new CSSEngineEvent(this , target, props);
3346: for (int i = 0; i < len; i++) {
3347: ll[i].propertiesChanged(evt);
3348: }
3349: }
3350: }
3351:
3352: // Dynamic updates ////////////////////////////////////////////////////
3353:
3354: /**
3355: * Called when the inline style of the given element has been updated.
3356: */
3357: protected void inlineStyleAttributeUpdated(CSSStylableElement elt,
3358: StyleMap style, MutationEvent evt) {
3359: boolean[] updated = styleDeclarationUpdateHandler.updatedProperties;
3360: for (int i = getNumberOfProperties() - 1; i >= 0; --i) {
3361: updated[i] = false;
3362: }
3363:
3364: switch (evt.getAttrChange()) {
3365: case MutationEvent.ADDITION:
3366: case MutationEvent.MODIFICATION:
3367: String decl = evt.getNewValue();
3368: // System.err.println("Inline Style Update: '" + decl + "'");
3369: if (decl.length() > 0) {
3370: element = elt;
3371: try {
3372: parser
3373: .setSelectorFactory(CSSSelectorFactory.INSTANCE);
3374: parser.setConditionFactory(cssConditionFactory);
3375: styleDeclarationUpdateHandler.styleMap = style;
3376: parser
3377: .setDocumentHandler(styleDeclarationUpdateHandler);
3378: // <rave>
3379: // BEGIN RAVE MODIFICATIONS
3380: styleDeclarationUpdateHandler.location = elt;
3381: styleDeclarationUpdateHandler.lineno = -1;
3382: // END RAVE MODIFICATIONS
3383: // </rave>
3384: parser.parseStyleDeclaration(decl);
3385: styleDeclarationUpdateHandler.styleMap = null;
3386: } catch (Exception e) {
3387: String m = e.getMessage();
3388: if (m == null)
3389: m = "";
3390: String u = ((documentURI == null) ? "<unknown>"
3391: : documentURI.toString());
3392: String s = Messages.formatMessage(
3393: "style.syntax.error.at", new Object[] { u,
3394: styleLocalName, decl, m });
3395: DOMException de = new DOMException(
3396: DOMException.SYNTAX_ERR, s);
3397: // <rave>
3398: // BEGIN RAVE MODIFICATIONS
3399: de.initCause(e);
3400: // END RAVE MODIFICATIONS
3401: // </rave>
3402: if (userAgent == null)
3403: throw de;
3404: userAgent.displayError(de);
3405: } finally {
3406: element = null;
3407: cssBaseURI = null;
3408: }
3409: }
3410:
3411: // Fall through
3412: case MutationEvent.REMOVAL:
3413: boolean removed = false;
3414:
3415: if (evt.getPrevValue() != null
3416: && evt.getPrevValue().length() > 0) {
3417: // Check if the style map has cascaded styles which
3418: // come from the inline style attribute.
3419: for (int i = getNumberOfProperties() - 1; i >= 0; --i) {
3420: if (style.isComputed(i)
3421: && style.getOrigin(i) == StyleMap.INLINE_AUTHOR_ORIGIN
3422: && !updated[i]) {
3423: removed = true;
3424: updated[i] = true;
3425: }
3426: }
3427: }
3428:
3429: if (removed) {
3430: invalidateProperties(elt, null, updated, true);
3431: } else {
3432: int count = 0;
3433: // Invalidate the relative values
3434: boolean fs = (fontSizeIndex == -1) ? false
3435: : updated[fontSizeIndex];
3436: boolean lh = (lineHeightIndex == -1) ? false
3437: : updated[lineHeightIndex];
3438: boolean cl = (colorIndex == -1) ? false
3439: : updated[colorIndex];
3440:
3441: for (int i = getNumberOfProperties() - 1; i >= 0; --i) {
3442: if (updated[i]) {
3443: count++;
3444: } else if ((fs && style.isFontSizeRelative(i))
3445: || (lh && style.isLineHeightRelative(i))
3446: || (cl && style.isColorRelative(i))) {
3447: updated[i] = true;
3448: clearComputedValue(style, i);
3449: count++;
3450: }
3451: }
3452:
3453: if (count > 0) {
3454: int[] props = new int[count];
3455: count = 0;
3456: for (int i = getNumberOfProperties() - 1; i >= 0; --i) {
3457: if (updated[i]) {
3458: props[count++] = i;
3459: }
3460: }
3461: invalidateProperties(elt, props, null, true);
3462: }
3463: }
3464: break;
3465:
3466: default:
3467: // Must not happen
3468: throw new InternalError("Invalid attrChangeType");
3469: }
3470: }
3471:
3472: private static void clearComputedValue(StyleMap style, int n) {
3473: if (style.isNullCascaded(n)) {
3474: style.putValue(n, null);
3475: } else {
3476: Value v = style.getValue(n);
3477: if (v instanceof ComputedValue) {
3478: ComputedValue cv = (ComputedValue) v;
3479: v = cv.getCascadedValue();
3480: style.putValue(n, v);
3481: }
3482: }
3483: style.putComputed(n, false);
3484: }
3485:
3486: /**
3487: * Invalidates all the properties of the given node.
3488: *
3489: */
3490: protected void invalidateProperties(Node node, int[] properties,
3491: boolean[] updated, boolean recascade) {
3492:
3493: if (!(node instanceof CSSStylableElement))
3494: return; // Not Stylable sub tree
3495:
3496: CSSStylableElement elt = (CSSStylableElement) node;
3497: StyleMap style = elt.getComputedStyleMap(null);
3498: if (style == null)
3499: return; // Nothing to invalidate.
3500:
3501: boolean[] diffs = new boolean[getNumberOfProperties()];
3502: if (updated != null) {
3503: for (int i = 0; i < updated.length; i++) {
3504: diffs[i] = updated[i];
3505: }
3506: }
3507: if (properties != null) {
3508: for (int i = 0; i < properties.length; i++) {
3509: diffs[properties[i]] = true;
3510: }
3511: }
3512: int count = 0;
3513: if (!recascade) {
3514: for (int i = 0; i < diffs.length; i++) {
3515: if (diffs[i])
3516: count++;
3517: }
3518: } else {
3519: StyleMap newStyle = getCascadedStyleMap(elt, null);
3520: elt.setComputedStyleMap(null, newStyle);
3521: for (int i = 0; i < diffs.length; i++) {
3522: if (diffs[i]) {
3523: count++;
3524: continue; // Already marked changed.
3525: }
3526:
3527: // Value nv = getComputedStyle(elt, null, i);
3528: Value nv = newStyle.getValue(i);
3529: Value ov = null;
3530: if (!style.isNullCascaded(i)) {
3531: ov = style.getValue(i);
3532: if (ov instanceof ComputedValue) {
3533: ov = ((ComputedValue) ov).getCascadedValue();
3534: }
3535: }
3536:
3537: if (nv == ov)
3538: continue;
3539: if ((nv != null) && (ov != null)) {
3540: if (nv.equals(ov))
3541: continue;
3542: String ovCssText = ov.getCssText();
3543: String nvCssText = nv.getCssText();
3544: if ((nvCssText == ovCssText)
3545: || ((nvCssText != null) && nvCssText
3546: .equals(ovCssText)))
3547: continue;
3548: }
3549: count++;
3550: diffs[i] = true;
3551: }
3552: }
3553: int[] props = null;
3554: if (count != 0) {
3555: props = new int[count];
3556: count = 0;
3557: for (int i = 0; i < diffs.length; i++) {
3558: if (diffs[i])
3559: props[count++] = i;
3560: }
3561: }
3562: propagateChanges(elt, props, recascade);
3563: }
3564:
3565: /**
3566: * Propagates the changes that occurs on the parent of the given node.
3567: * Props is a list of known 'changed' properties.
3568: * If recascade is true then the stylesheets will be applied
3569: * again to see if the any new rules apply (or old rules don't
3570: * apply).
3571: */
3572: protected void propagateChanges(Node node, int[] props,
3573: boolean recascade) {
3574: if (!(node instanceof CSSStylableElement))
3575: return;
3576: CSSStylableElement elt = (CSSStylableElement) node;
3577: StyleMap style = elt.getComputedStyleMap(null);
3578: if (style != null) {
3579: boolean[] updated = styleDeclarationUpdateHandler.updatedProperties;
3580: for (int i = getNumberOfProperties() - 1; i >= 0; --i) {
3581: updated[i] = false;
3582: }
3583: if (props != null) {
3584: for (int i = props.length - 1; i >= 0; --i) {
3585: int idx = props[i];
3586: updated[idx] = true;
3587: }
3588: }
3589:
3590: // Invalidate the relative values
3591: boolean fs = (fontSizeIndex == -1) ? false
3592: : updated[fontSizeIndex];
3593: boolean lh = (lineHeightIndex == -1) ? false
3594: : updated[lineHeightIndex];
3595: boolean cl = (colorIndex == -1) ? false
3596: : updated[colorIndex];
3597:
3598: int count = 0;
3599: for (int i = getNumberOfProperties() - 1; i >= 0; --i) {
3600: if (updated[i]) {
3601: count++;
3602: } else if ((fs && style.isFontSizeRelative(i))
3603: || (lh && style.isLineHeightRelative(i))
3604: || (cl && style.isColorRelative(i))) {
3605: updated[i] = true;
3606: clearComputedValue(style, i);
3607: count++;
3608: }
3609: }
3610:
3611: if (count == 0) {
3612: props = null;
3613: } else {
3614: props = new int[count];
3615: count = 0;
3616: for (int i = getNumberOfProperties() - 1; i >= 0; --i) {
3617: if (updated[i]) {
3618: props[count++] = i;
3619: }
3620: }
3621: firePropertiesChangedEvent(elt, props);
3622: }
3623: }
3624:
3625: int[] inherited = props;
3626: if (props != null) {
3627: // Filter out uninheritable properties when we
3628: // propogate to children.
3629: int count = 0;
3630: for (int i = 0; i < props.length; i++) {
3631: ValueManager vm = valueManagers[props[i]];
3632: if (vm.isInheritedProperty())
3633: count++;
3634: else
3635: props[i] = -1;
3636: }
3637:
3638: if (count == 0) {
3639: // nothing to propogate for sure
3640: inherited = null;
3641: } else {
3642: inherited = new int[count];
3643: count = 0;
3644: for (int i = 0; i < props.length; i++)
3645: if (props[i] != -1)
3646: inherited[count++] = props[i];
3647: }
3648: }
3649:
3650: CSSImportedElementRoot ier = getImportedChild(node);
3651: if (ier != null) {
3652: Node c = ier.getFirstChild();
3653: // Don't recascade trees that have been imported.
3654: // If you do it will use the stylesheets from this
3655: // document instead of the original document. Also
3656: // currently there isn't any supported way to modify
3657: // the content imported from the other document so
3658: // the cascade can't change.
3659: invalidateProperties(c, inherited, null, ier.getIsLocal());
3660: }
3661: for (Node n = node.getFirstChild(); n != null; n = n
3662: .getNextSibling()) {
3663: invalidateProperties(n, inherited, null, recascade);
3664: }
3665: }
3666:
3667: /**
3668: * To parse a style declaration and update a StyleMap.
3669: */
3670: protected class StyleDeclarationUpdateHandler extends
3671: DocumentAdapter implements ShorthandManager.PropertyHandler {
3672: public StyleMap styleMap;
3673: public boolean[] updatedProperties = new boolean[getNumberOfProperties()];
3674:
3675: // <rave>
3676: // BEGIN RAVE MODIFICATIONS
3677: Object location;
3678: int lineno;
3679:
3680: // ENBD RAVE MODIFICATIONS
3681: // </rave>
3682: /**
3683: * <b>SAC</b>: Implements {@link
3684: * DocumentHandler#property(String,LexicalUnit,boolean)}.
3685: */
3686: public void property(String name, LexicalUnit value,
3687: boolean important) throws CSSException {
3688: int i = getPropertyIndex(name);
3689: if (i == -1) {
3690: i = getShorthandIndex(name);
3691: if (i == -1) {
3692: // Unknown property
3693: return;
3694: }
3695: // <rave>
3696: // BEGIN RAVE MODIFICATIONS
3697: try {
3698: expandingShorthandProperty = i;
3699: // END RAVE MODIFICATIONS
3700: // </rave>
3701: shorthandManagers[i].setValues(CSSEngine.this ,
3702: this , value, important);
3703: // <rave>
3704: // BEGIN RAVE MODIFICATIONS
3705: expandingShorthandProperty = -1;
3706: } catch (DOMException e) {
3707: // Something bad happened
3708: displayError(e, location, lineno + parser.getLine()
3709: - 1, parser.getColumn());
3710: // Continue processing - we're supposed to ignore
3711: // errant declarations!!!
3712: }
3713: // END RAVE MODIFICATIONS
3714: // </rave>
3715: } else {
3716: if (styleMap.isImportant(i)) {
3717: // The previous value is important, and a value
3718: // from a style attribute cannot be important...
3719: return;
3720: }
3721:
3722: updatedProperties[i] = true;
3723:
3724: Value v = valueManagers[i].createValue(value,
3725: CSSEngine.this );
3726: // <rave>
3727: // BEGIN RAVE MODIFICATIONS
3728: // Track the value source
3729: if (v instanceof AbstractValue) { // Should I add to Value interface???
3730: AbstractValue av = (AbstractValue) v;
3731: av.setLocation(location);
3732: av.setLineNumber(lineno + parser.getLine() - 1);
3733: }
3734: // END RAVE MODIFICATIONS
3735: // </rave>
3736: styleMap.putMask(i, (short) 0);
3737: styleMap.putValue(i, v);
3738: styleMap.putOrigin(i, StyleMap.INLINE_AUTHOR_ORIGIN);
3739: }
3740: }
3741: }
3742:
3743: /**
3744: * Called when a non-CSS presentational hint has been updated.
3745: */
3746: protected void nonCSSPresentationalHintUpdated(
3747: CSSStylableElement elt, StyleMap style, String property,
3748: MutationEvent evt) {
3749: // System.err.println("update: " + property);
3750: int idx = getPropertyIndex(property);
3751:
3752: if (style.isImportant(idx)) {
3753: // The current value is important, and a value
3754: // from an XML attribute cannot be important...
3755: return;
3756: }
3757:
3758: switch (style.getOrigin(idx)) {
3759: case StyleMap.AUTHOR_ORIGIN:
3760: case StyleMap.INLINE_AUTHOR_ORIGIN:
3761: // The current value has a greater priority
3762: return;
3763: }
3764:
3765: switch (evt.getAttrChange()) {
3766: case MutationEvent.ADDITION:
3767: case MutationEvent.MODIFICATION:
3768: element = elt;
3769: try {
3770: LexicalUnit lu;
3771: lu = parser.parsePropertyValue(evt.getNewValue());
3772: ValueManager vm = valueManagers[idx];
3773: Value v = vm.createValue(lu, CSSEngine.this );
3774: style.putMask(idx, (short) 0);
3775: style.putValue(idx, v);
3776: style.putOrigin(idx, StyleMap.NON_CSS_ORIGIN);
3777: } catch (Exception e) {
3778: String m = e.getMessage();
3779: if (m == null)
3780: m = "";
3781: String u = ((documentURI == null) ? "<unknown>"
3782: : documentURI.toString());
3783: String s = Messages.formatMessage(
3784: "property.syntax.error.at", new Object[] { u,
3785: property, evt.getNewValue(), m });
3786: DOMException de = new DOMException(
3787: DOMException.SYNTAX_ERR, s);
3788: // <rave>
3789: // BEGIN RAVE MODIFICATIONS
3790: de.initCause(e);
3791: // END RAVE MODIFICATIONS
3792: // </rave>
3793: if (userAgent == null)
3794: throw de;
3795: userAgent.displayError(de);
3796: } finally {
3797: element = null;
3798: cssBaseURI = null;
3799: }
3800: break;
3801:
3802: case MutationEvent.REMOVAL: {
3803: int[] invalid = { idx };
3804: invalidateProperties(elt, invalid, null, true);
3805: return;
3806: }
3807: }
3808:
3809: boolean[] updated = styleDeclarationUpdateHandler.updatedProperties;
3810: for (int i = getNumberOfProperties() - 1; i >= 0; --i) {
3811: updated[i] = false;
3812: }
3813: updated[idx] = true;
3814:
3815: // Invalidate the relative values
3816: boolean fs = idx == fontSizeIndex;
3817: boolean lh = idx == lineHeightIndex;
3818: boolean cl = idx == colorIndex;
3819: int count = 0;
3820:
3821: for (int i = getNumberOfProperties() - 1; i >= 0; --i) {
3822: if (updated[i]) {
3823: count++;
3824: } else if ((fs && style.isFontSizeRelative(i))
3825: || (lh && style.isLineHeightRelative(i))
3826: || (cl && style.isColorRelative(i))) {
3827: updated[i] = true;
3828: clearComputedValue(style, i);
3829: count++;
3830: }
3831: }
3832:
3833: int[] props = new int[count];
3834: count = 0;
3835: for (int i = getNumberOfProperties() - 1; i >= 0; --i) {
3836: if (updated[i]) {
3837: props[count++] = i;
3838: }
3839: }
3840:
3841: invalidateProperties(elt, props, null, true);
3842: }
3843:
3844: /**
3845: * To handle the insertion of a CSSStyleSheetNode in the
3846: * associated document.
3847: */
3848: protected class DOMNodeInsertedListener implements EventListener {
3849: public void handleEvent(Event evt) {
3850: EventTarget et = evt.getTarget();
3851: if (et instanceof CSSStyleSheetNode) {
3852: styleSheetNodes = null;
3853: // Invalidate all the CSSStylableElements in the document.
3854: invalidateProperties(document.getDocumentElement(),
3855: null, null, true);
3856: return;
3857: }
3858: if (et instanceof CSSStylableElement) {
3859: // Invalidate the CSSStylableElement siblings, to
3860: // correctly match the adjacent selectors and
3861: // first-child pseudo-class.
3862: for (Node n = ((Node) evt.getTarget()).getNextSibling(); n != null; n = n
3863: .getNextSibling()) {
3864: invalidateProperties(n, null, null, true);
3865: }
3866: }
3867: }
3868: }
3869:
3870: /**
3871: * To handle the removal of a CSSStyleSheetNode from the
3872: * associated document.
3873: */
3874: protected class DOMNodeRemovedListener implements EventListener {
3875: public void handleEvent(Event evt) {
3876: EventTarget et = evt.getTarget();
3877: if (et instanceof CSSStyleSheetNode) {
3878: // Wait for the DOMSubtreeModified to do the invalidations
3879: // because at this time the node is in the tree.
3880: styleSheetRemoved = true;
3881: } else if (et instanceof CSSStylableElement) {
3882: // Wait for the DOMSubtreeModified to do the invalidations
3883: // because at this time the node is in the tree.
3884: removedStylableElementSibling = ((Node) et)
3885: .getNextSibling();
3886: }
3887: // Clears the computed styles in the removed tree.
3888: disposeStyleMaps((Node) et);
3889: }
3890: }
3891:
3892: /**
3893: * To handle the removal of a CSSStyleSheetNode from the
3894: * associated document.
3895: */
3896: protected class DOMSubtreeModifiedListener implements EventListener {
3897: public void handleEvent(Event evt) {
3898: if (styleSheetRemoved) {
3899: styleSheetRemoved = false;
3900: styleSheetNodes = null;
3901:
3902: // Invalidate all the CSSStylableElements in the document.
3903: invalidateProperties(document.getDocumentElement(),
3904: null, null, true);
3905: } else if (removedStylableElementSibling != null) {
3906: // Invalidate the CSSStylableElement siblings, to
3907: // correctly match the adjacent selectors and
3908: // first-child pseudo-class.
3909: for (Node n = removedStylableElementSibling; n != null; n = n
3910: .getNextSibling()) {
3911: invalidateProperties(n, null, null, true);
3912: }
3913: removedStylableElementSibling = null;
3914: }
3915: }
3916: }
3917:
3918: /**
3919: * To handle the modification of a CSSStyleSheetNode.
3920: */
3921: protected class DOMCharacterDataModifiedListener implements
3922: EventListener {
3923: public void handleEvent(Event evt) {
3924: Node n = (Node) evt.getTarget();
3925: if (n.getParentNode() instanceof CSSStyleSheetNode) {
3926: styleSheetNodes = null;
3927: // Invalidate all the CSSStylableElements in the document.
3928: invalidateProperties(document.getDocumentElement(),
3929: null, null, true);
3930: }
3931: }
3932: }
3933:
3934: /**
3935: * To handle the element attributes modification in the associated
3936: * document.
3937: */
3938: protected class DOMAttrModifiedListener implements EventListener {
3939: public void handleEvent(Event evt) {
3940: EventTarget et = evt.getTarget();
3941: if (!(et instanceof CSSStylableElement)) {
3942: // Not a stylable element.
3943: return;
3944: }
3945:
3946: MutationEvent mevt = (MutationEvent) evt;
3947: if (mevt.getNewValue().equals(mevt.getPrevValue()))
3948: return; // no change really...
3949:
3950: Node attr = mevt.getRelatedNode();
3951: String attrNS = attr.getNamespaceURI();
3952: String name = (attrNS == null) ? attr.getNodeName() : attr
3953: .getLocalName();
3954:
3955: CSSStylableElement elt = (CSSStylableElement) et;
3956: // <rave>
3957: // BEGIN RAVE MODIFICATIONS
3958:
3959: try {
3960:
3961: // END RAVE MODIFICATIONS
3962: // </rave>
3963: StyleMap style = elt.getComputedStyleMap(null);
3964: if (style != null) {
3965: if ((attrNS == null && styleNamespaceURI == null)
3966: || (attrNS != null && attrNS
3967: .equals(styleNamespaceURI))) {
3968: if (name.equals(styleLocalName)) {
3969: // The style declaration attribute has been modified.
3970: inlineStyleAttributeUpdated(elt, style,
3971: mevt);
3972: return;
3973: }
3974: }
3975: // <rave>
3976: // BEGIN RAVE MODIFICATIONS
3977: // Note - we've gotta update this code to do proper
3978: // nonpresentational hints updates!
3979: // END RAVE MODIFICATIONS
3980: // </rave>
3981: if (nonCSSPresentationalHints != null) {
3982: if ((attrNS == null && nonCSSPresentationalHintsNamespaceURI == null)
3983: || (attrNS != null && attrNS
3984: .equals(nonCSSPresentationalHintsNamespaceURI))) {
3985: if (nonCSSPresentationalHints
3986: .contains(name)) {
3987: // The 'name' attribute which represents a non CSS
3988: // presentational hint has been modified.
3989: nonCSSPresentationalHintUpdated(elt,
3990: style, name, mevt);
3991: return;
3992: }
3993: }
3994: }
3995: }
3996:
3997: if (selectorAttributes != null
3998: && selectorAttributes.contains(name)) {
3999: // An attribute has been modified, invalidate all the
4000: // properties to correctly match attribute selectors.
4001: invalidateProperties(elt, null, null, true);
4002: for (Node n = elt.getNextSibling(); n != null; n = n
4003: .getNextSibling()) {
4004: invalidateProperties(n, null, null, true);
4005: }
4006: }
4007: // <rave>
4008: // BEGIN RAVE MODIFICATIONS
4009: } catch (DOMException e) {
4010: // Something bad happened
4011: displayError(e, elt, parser.getLine() - 1, parser
4012: .getColumn());
4013: // Continue processing - we're supposed to ignore
4014: // errant declarations!!!
4015:
4016: }
4017: // END RAVE MODIFICATIONS
4018: // </rave>
4019: }
4020: }
4021: }
|