KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > lobobrowser > html > domimpl > HTMLDocumentImpl


1 /*
2     GNU LESSER GENERAL PUBLIC LICENSE
3     Copyright (C) 2006 The Lobo Project
4
5     This library is free software; you can redistribute it and/or
6     modify it under the terms of the GNU Lesser General Public
7     License as published by the Free Software Foundation; either
8     version 2.1 of the License, or (at your option) any later version.
9
10     This library is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13     Lesser General Public License for more details.
14
15     You should have received a copy of the GNU Lesser General Public
16     License along with this library; if not, write to the Free Software
17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
19     Contact info: xamjadmin@users.sourceforge.net
20 */

21 /*
22  * Created on Sep 3, 2005
23  */

24 package org.lobobrowser.html.domimpl;
25
26 import org.lobobrowser.html.*;
27 import org.lobobrowser.html.io.*;
28 import org.lobobrowser.html.js.*;
29 import org.lobobrowser.html.parser.HtmlParser;
30 import org.lobobrowser.html.style.*;
31 import org.lobobrowser.util.*;
32 import org.lobobrowser.util.io.EmptyReader;
33 import org.w3c.dom.*;
34 import org.w3c.dom.css.CSSStyleSheet;
35 import org.w3c.dom.html2.*;
36 import org.xml.sax.ErrorHandler JavaDoc;
37 import org.xml.sax.SAXException JavaDoc;
38 import org.mozilla.javascript.Function;
39
40 import java.io.*;
41 import java.net.MalformedURLException JavaDoc;
42 import java.net.URL JavaDoc;
43 import java.util.*;
44 import java.util.logging.*;
45 import java.security.*;
46
47 public class HTMLDocumentImpl extends NodeImpl implements HTMLDocument {
48     private static final Logger logger = Logger.getLogger(HTMLDocumentImpl.class.getName());
49     private final ElementFactory factory;
50     private final HtmlRendererContext rcontext;
51     private final UserAgentContext ucontext;
52     private final Window window;
53     private final Map elementsById = new WeakValueHashMap();
54     private String JavaDoc documentURI;
55     private java.net.URL JavaDoc documentURL;
56     
57     private WritableLineReader reader;
58
59     public HTMLDocumentImpl(HtmlRendererContext rcontext) {
60         this(rcontext.getUserAgentContext(), rcontext, null, null);
61     }
62
63     public HTMLDocumentImpl(UserAgentContext ucontext) {
64         this(ucontext, null, null, null);
65     }
66
67     public HTMLDocumentImpl(final UserAgentContext ucontext, final HtmlRendererContext rcontext, WritableLineReader reader, String JavaDoc documentURI) {
68         this.factory = ElementFactory.getInstance();
69         this.rcontext = rcontext;
70         this.ucontext = ucontext;
71         this.reader = reader;
72         this.documentURI = documentURI;
73         try {
74             java.net.URL JavaDoc docURL = new java.net.URL JavaDoc(documentURI);
75             SecurityManager JavaDoc sm = System.getSecurityManager();
76             if(sm != null) {
77                 // Do not allow creation of HTMLDocumentImpl if there's
78
// no permission to connect to the host of the URL.
79
// This is so that cookies cannot be written arbitrarily
80
// with setCookie() method.
81
sm.checkPermission(new java.net.SocketPermission JavaDoc(docURL.getHost(), "connect"));
82             }
83             this.documentURL = docURL;
84             this.domain = docURL.getHost();
85         } catch(java.net.MalformedURLException JavaDoc mfu) {
86             logger.warning("HTMLDocumentImpl(): Document URI [" + documentURI + "] is malformed.");
87         }
88         this.document = this;
89         // Get Window object
90
Window window;
91         if(rcontext != null) {
92             window = Window.getWindow(rcontext);
93         }
94         else {
95             // Plain parsers may use Javascript too.
96
window = new Window(null, ucontext);
97         }
98         // Window must be retained or it will be garbage collected.
99
this.window = window;
100         window.setDocument(this);
101         // Set up Javascript scope
102
this.setUserData(Executor.SCOPE_KEY, window.getWindowScope(), null);
103     }
104
105     String JavaDoc getDocumentHost() {
106         URL JavaDoc docUrl = this.documentURL;
107         return docUrl == null ? null : docUrl.getHost();
108     }
109
110     public URL JavaDoc getDocumentURL() {
111         //TODO: Security considerations?
112
return this.documentURL;
113     }
114
115     /**
116      * Caller should synchronize on document.
117      */

118     void setElementById(String JavaDoc id, Element element) {
119         synchronized(this) {
120             this.elementsById.put(id, element);
121         }
122     }
123     
124     void removeElementById(String JavaDoc id) {
125         synchronized(this) {
126             this.elementsById.remove(id);
127         }
128     }
129     
130     private volatile String JavaDoc baseURI;
131
132     /* (non-Javadoc)
133      * @see org.xamjwg.html.domimpl.NodeImpl#getbaseURI()
134      */

135     public String JavaDoc getBaseURI() {
136         String JavaDoc buri = this.baseURI;
137         return buri == null ? this.documentURI : buri;
138     }
139
140     public void setBaseURI(String JavaDoc value) {
141         this.baseURI = value;
142     }
143
144     private String JavaDoc defaultTarget;
145     
146     public String JavaDoc getDefaultTarget() {
147         return this.defaultTarget;
148     }
149     
150     public void setDefaultTarget(String JavaDoc value) {
151         this.defaultTarget = value;
152     }
153     
154     public String JavaDoc getTextContent() throws DOMException {
155         return null;
156     }
157     
158     public void setTextContent(String JavaDoc textContent) throws DOMException {
159         // NOP, per spec
160
}
161
162     private String JavaDoc title;
163     
164     public String JavaDoc getTitle() {
165         return this.title;
166     }
167
168     public void setTitle(String JavaDoc title) {
169         this.title = title;
170     }
171
172     private String JavaDoc referrer;
173     
174     public String JavaDoc getReferrer() {
175         return this.referrer;
176     }
177     
178     public void setReferrer(String JavaDoc value) {
179         this.referrer = value;
180     }
181
182     private String JavaDoc domain;
183     
184     public String JavaDoc getDomain() {
185         return this.domain;
186     }
187
188     public void setDomain(String JavaDoc domain) {
189         String JavaDoc oldDomain = this.domain;
190         if(oldDomain != null && Domains.isValidCookieDomain(domain, oldDomain)) {
191             this.domain = domain;
192         }
193         else {
194             throw new SecurityException JavaDoc("Cannot set domain to '" + domain + "' when current domain is '" + oldDomain + "'");
195         }
196     }
197     
198     public HTMLElement getBody() {
199         synchronized(this) {
200             return this.body;
201         }
202     }
203
204     private HTMLCollection images;
205     private HTMLCollection applets;
206     private HTMLCollection links;
207     private HTMLCollection forms;
208     private HTMLCollection anchors;
209     private HTMLCollection frames;
210     
211     public HTMLCollection getImages() {
212         synchronized(this) {
213             if(this.images == null) {
214                 this.images = new FilteredHTMLCollectionImpl(this, this.elementsById, new ImageFilter(), this.treeLock);
215             }
216             return this.images;
217         }
218     }
219
220     public HTMLCollection getApplets() {
221         synchronized(this) {
222             if(this.applets == null) {
223                 //TODO: Should include OBJECTs that are applets?
224
this.applets = new FilteredHTMLCollectionImpl(this, this.elementsById, new AppletFilter(), this.treeLock);
225             }
226             return this.applets;
227         }
228     }
229
230     public HTMLCollection getLinks() {
231         synchronized(this) {
232             if(this.links == null) {
233                 this.links = new FilteredHTMLCollectionImpl(this, this.elementsById, new LinkFilter(), this.treeLock);
234             }
235             return this.links;
236         }
237     }
238
239     public HTMLCollection getForms() {
240         synchronized(this) {
241             if(this.forms == null) {
242                 this.forms = new FilteredHTMLCollectionImpl(this, this.elementsById, new FormFilter(), this.treeLock);
243             }
244             return this.forms;
245         }
246     }
247
248     public HTMLCollection getFrames() {
249         synchronized(this) {
250             if(this.frames == null) {
251                 this.frames = new FilteredHTMLCollectionImpl(this, this.elementsById, new FrameFilter(), this.treeLock);
252             }
253             return this.frames;
254         }
255     }
256
257     public HTMLCollection getAnchors() {
258         synchronized(this) {
259             if(this.anchors == null) {
260                 this.anchors = new FilteredHTMLCollectionImpl(this, this.elementsById, new AnchorFilter(), this.treeLock);
261             }
262             return this.anchors;
263         }
264     }
265
266     public String JavaDoc getCookie() {
267         SecurityManager JavaDoc sm = System.getSecurityManager();
268         if(sm != null) {
269             return (String JavaDoc) AccessController.doPrivileged(new PrivilegedAction() {
270                 // Justification: A caller (e.g. Google Analytics script)
271
// might want to get cookies from the parent document.
272
// If the caller has access to the document, it appears
273
// they should be able to get cookies on that document.
274
// Note that this Document instance cannot be created
275
// with an arbitrary URL.
276
public Object JavaDoc run() {
277                     return ucontext.getCookie(documentURL);
278                 }
279             });
280         }
281         else {
282             return this.ucontext.getCookie(this.documentURL);
283         }
284     }
285
286     public void setCookie(final String JavaDoc cookie) throws DOMException {
287         SecurityManager JavaDoc sm = System.getSecurityManager();
288         if(sm != null) {
289             AccessController.doPrivileged(new PrivilegedAction() {
290                 // Justification: A caller (e.g. Google Analytics script)
291
// might want to set cookies on the parent document.
292
// If the caller has access to the document, it appears
293
// they should be able to set cookies on that document.
294
// Note that this Document instance cannot be created
295
// with an arbitrary URL.
296
public Object JavaDoc run() {
297                     ucontext.setCookie(documentURL, cookie);
298                     return null;
299                 }
300             });
301         }
302         else {
303             this.ucontext.setCookie(this.documentURL, cookie);
304         }
305     }
306     
307     public void open() {
308         synchronized(this.treeLock) {
309             if(this.reader != null) {
310                 if(this.reader instanceof LocalWritableLineReader) {
311                     try {
312                         this.reader.close();
313                     } catch(IOException ioe) {
314                         //ignore
315
}
316                     this.reader = null;
317                 }
318                 else {
319                     // Already open, return.
320
// Do not close http/file documents in progress.
321
return;
322                 }
323             }
324             this.removeAllChildrenImpl();
325             this.reader = new LocalWritableLineReader(new EmptyReader());
326         }
327     }
328
329     /**
330      * Loads the document from the reader provided when it was constructed.
331      * It then closes the reader.
332      * @throws IOException
333      * @throws SAXException
334      * @throws UnsupportedEncodingException
335      */

336     public void load() throws IOException,SAXException JavaDoc,UnsupportedEncodingException {
337         this.load(true);
338     }
339     
340     public void load(boolean closeReader) throws IOException,SAXException JavaDoc,UnsupportedEncodingException {
341         WritableLineReader reader;
342         synchronized(this.treeLock) {
343             this.removeAllChildrenImpl();
344             this.setTitle(null);
345             this.setBaseURI(null);
346             this.setDefaultTarget(null);
347             this.styleSheets.clear();
348             this.styleSheetAggregator = null;
349             reader = this.reader;
350         }
351         if(reader != null) {
352             try {
353                 ErrorHandler JavaDoc errorHandler = new LocalErrorHandler();
354                 String JavaDoc systemId = this.documentURI;
355                 String JavaDoc publicId = systemId;
356                 HtmlParser parser = new HtmlParser(this.ucontext, this, errorHandler, publicId, systemId);
357                 parser.parse(reader);
358             } finally {
359                 if(closeReader) {
360                     try {
361                         reader.close();
362                     } catch(Exception JavaDoc err) {
363                         logger.log(Level.WARNING,"load(): Unable to close stream", err);
364                     }
365                     synchronized(this.treeLock) {
366                         this.reader = null;
367                     }
368                 }
369             }
370         }
371     }
372     
373     public void close() {
374         synchronized(this.treeLock) {
375             if(this.reader instanceof LocalWritableLineReader) {
376                 try {
377                     this.reader.close();
378                 } catch(java.io.IOException JavaDoc ioe) {
379                     // ignore
380
}
381                 this.reader = null;
382             }
383             else {
384                 // do nothing - could be parsing document off the web.
385
}
386             //TODO: cause it to render
387
}
388     }
389
390     public void write(String JavaDoc text) {
391         synchronized(this.treeLock) {
392             if(this.reader != null) {
393                 try {
394                     // This can end up in openBufferChanged
395
this.reader.write(text);
396                 } catch(IOException ioe) {
397                     //ignore
398
}
399             }
400         }
401     }
402
403     public void writeln(String JavaDoc text) {
404         synchronized(this.treeLock) {
405             if(this.reader != null) {
406                 try {
407                     // This can end up in openBufferChanged
408
this.reader.write(text + "\r\n");
409                 } catch(IOException ioe) {
410                     //ignore
411
}
412             }
413         }
414     }
415
416     private void openBufferChanged(String JavaDoc text) {
417         // Assumed to execute in a lock
418
// Assumed that text is not broken up HTML.
419
ErrorHandler JavaDoc errorHandler = new LocalErrorHandler();
420         String JavaDoc systemId = this.documentURI;
421         String JavaDoc publicId = systemId;
422         HtmlParser parser = new HtmlParser(this.ucontext, this, errorHandler, publicId, systemId);
423         StringReader strReader = new StringReader(text);
424         try {
425             // This sets up another Javascript scope Window. Does it matter?
426
parser.parse(strReader);
427         } catch(Exception JavaDoc err) {
428             this.warn("Unable to parse written HTML text. BaseURI=[" + this.getBaseURI() + "].", err);
429         }
430     }
431     
432     /**
433      * Gets the collection of elements whose <code>name</code>
434      * attribute is <code>elementName</code>.
435      */

436     public NodeList getElementsByName(String JavaDoc elementName) {
437         return this.getNodeList(new ElementNameFilter(elementName));
438     }
439     
440     private DocumentType doctype;
441     
442     public DocumentType getDoctype() {
443         return this.doctype;
444     }
445
446     public void setDoctype(DocumentType doctype) {
447         this.doctype = doctype;
448     }
449     
450     public Element getDocumentElement() {
451         synchronized(this.treeLock) {
452             ArrayList nl = this.nodeList;
453             if(nl != null) {
454                 Iterator i = nl.iterator();
455                 while(i.hasNext()) {
456                     Object JavaDoc node = i.next();
457                     if(node instanceof Element) {
458                         return (Element) node;
459                     }
460                 }
461             }
462             return null;
463         }
464     }
465
466     public Element createElement(String JavaDoc tagName)
467             throws DOMException {
468         return this.factory.createElement(this, tagName);
469     }
470     
471     /* (non-Javadoc)
472      * @see org.w3c.dom.Document#createDocumentFragment()
473      */

474     public DocumentFragment createDocumentFragment() {
475         //TODO: According to documentation, when a document
476
//fragment is added to a node, its children are added,
477
//not itself.
478
DocumentFragmentImpl node = new DocumentFragmentImpl();
479         node.setOwnerDocument(this);
480         return node;
481     }
482
483     public Text createTextNode(String JavaDoc data) {
484         TextImpl node = new TextImpl(data);
485         node.setOwnerDocument(this);
486         return node;
487     }
488
489     public Comment createComment(String JavaDoc data) {
490         CommentImpl node = new CommentImpl(data);
491         node.setOwnerDocument(this);
492         return node;
493     }
494
495     public CDATASection createCDATASection(String JavaDoc data)
496             throws DOMException {
497         CDataSectionImpl node = new CDataSectionImpl(data);
498         node.setOwnerDocument(this);
499         return node;
500     }
501
502     public ProcessingInstruction createProcessingInstruction(
503             String JavaDoc target, String JavaDoc data) throws DOMException {
504         throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "HTML document");
505     }
506
507     public Attr createAttribute(String JavaDoc name) throws DOMException {
508         return new AttrImpl(name);
509     }
510
511     public EntityReference createEntityReference(String JavaDoc name)
512             throws DOMException {
513         throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "HTML document");
514     }
515
516     /**
517      * Gets all elements that match the given tag name.
518      * @param tagname The element tag name or an asterisk
519      * character (*) to match all elements.
520      */

521     public NodeList getElementsByTagName(String JavaDoc tagname) {
522         if("*".equals(tagname)) {
523             return this.getNodeList(new ElementFilter());
524         }
525         else {
526             return this.getNodeList(new TagNameFilter(tagname));
527         }
528     }
529
530     public Node importNode(Node importedNode, boolean deep)
531             throws DOMException {
532         throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Not implemented");
533     }
534
535     public Element createElementNS(String JavaDoc namespaceURI,
536             String JavaDoc qualifiedName) throws DOMException {
537         throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "HTML document");
538     }
539
540     public Attr createAttributeNS(String JavaDoc namespaceURI,
541             String JavaDoc qualifiedName) throws DOMException {
542         throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "HTML document");
543     }
544
545     public NodeList getElementsByTagNameNS(String JavaDoc namespaceURI,
546             String JavaDoc localName) {
547         throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "HTML document");
548     }
549
550     public Element getElementById(String JavaDoc elementId) {
551         Element element;
552         synchronized(this) {
553             element = (Element) this.elementsById.get(elementId);
554         }
555         return element;
556     }
557
558     private final Map elementsByName = new HashMap(0);
559     
560     public Element namedItem(String JavaDoc name) {
561         Element element;
562         synchronized(this) {
563             element = (Element) this.elementsByName.get(name);
564         }
565         return element;
566     }
567     
568     void setNamedItem(String JavaDoc name, Element element) {
569         synchronized(this) {
570             this.elementsByName.put(name, element);
571         }
572     }
573     
574     void removeNamedItem(String JavaDoc name) {
575         synchronized(this) {
576             this.elementsByName.remove(name);
577         }
578     }
579
580     private String JavaDoc inputEncoding;
581     
582     public String JavaDoc getInputEncoding() {
583         return this.inputEncoding;
584     }
585     
586     private String JavaDoc xmlEncoding;
587
588     public String JavaDoc getXmlEncoding() {
589         return this.xmlEncoding;
590     }
591
592     private boolean xmlStandalone;
593     
594     public boolean getXmlStandalone() {
595         return this.xmlStandalone;
596     }
597
598     public void setXmlStandalone(boolean xmlStandalone) throws DOMException {
599         this.xmlStandalone = xmlStandalone;
600     }
601
602     private String JavaDoc xmlVersion = null;
603     
604     public String JavaDoc getXmlVersion() {
605         return this.xmlVersion;
606     }
607
608     public void setXmlVersion(String JavaDoc xmlVersion) throws DOMException {
609         this.xmlVersion = xmlVersion;
610     }
611
612     private boolean strictErrorChecking = true;
613     
614     public boolean getStrictErrorChecking() {
615         return this.strictErrorChecking;
616     }
617
618     public void setStrictErrorChecking(boolean strictErrorChecking) {
619         this.strictErrorChecking = strictErrorChecking;
620     }
621
622     public String JavaDoc getDocumentURI() {
623         return this.documentURI;
624     }
625
626     public void setDocumentURI(String JavaDoc documentURI) {
627         //TODO: Security considerations? Chaging documentURL?
628
this.documentURI = documentURI;
629     }
630
631     public Node adoptNode(Node source) throws DOMException {
632         if(source instanceof NodeImpl) {
633             NodeImpl node = (NodeImpl) source;
634             node.setOwnerDocument(this, true);
635             return node;
636         }
637         else {
638             throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Invalid Node implementation");
639         }
640     }
641
642     private DOMConfiguration domConfig;
643     
644     public DOMConfiguration getDomConfig() {
645         synchronized(this) {
646             if(this.domConfig == null) {
647                 this.domConfig = new DOMConfigurationImpl();
648             }
649             return this.domConfig;
650         }
651     }
652
653     public void normalizeDocument() {
654         //TODO: Normalization options from domConfig
655
synchronized(this.treeLock) {
656             this.visitImpl(new NodeVisitor() {
657                 public void visit(Node node) {
658                     node.normalize();
659                 }
660             });
661         }
662     }
663
664     public Node renameNode(Node n, String JavaDoc namespaceURI,
665             String JavaDoc qualifiedName) throws DOMException {
666         throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "No renaming");
667     }
668
669     private DOMImplementation domImplementation;
670     
671     /* (non-Javadoc)
672      * @see org.w3c.dom.Document#getImplementation()
673      */

674     public DOMImplementation getImplementation() {
675         synchronized(this) {
676             if(this.domImplementation == null) {
677                 this.domImplementation = new DOMImplementationImpl(this.ucontext);
678             }
679             return this.domImplementation;
680         }
681     }
682
683     /* (non-Javadoc)
684      * @see org.xamjwg.html.domimpl.NodeImpl#getLocalName()
685      */

686     public String JavaDoc getLocalName() {
687         // Always null for document
688
return null;
689     }
690
691     /* (non-Javadoc)
692      * @see org.xamjwg.html.domimpl.NodeImpl#getNodeName()
693      */

694     public String JavaDoc getNodeName() {
695         return "#document";
696     }
697
698     /* (non-Javadoc)
699      * @see org.xamjwg.html.domimpl.NodeImpl#getNodeType()
700      */

701     public short getNodeType() {
702         return Node.DOCUMENT_NODE;
703     }
704
705     /* (non-Javadoc)
706      * @see org.xamjwg.html.domimpl.NodeImpl#getNodeValue()
707      */

708     public String JavaDoc getNodeValue() throws DOMException {
709         // Always null for document
710
return null;
711     }
712
713     /* (non-Javadoc)
714      * @see org.xamjwg.html.domimpl.NodeImpl#setNodeValue(java.lang.String)
715      */

716     public void setNodeValue(String JavaDoc nodeValue) throws DOMException {
717         throw new DOMException(DOMException.INVALID_MODIFICATION_ERR, "Cannot set node value of document");
718     }
719     
720     public final HtmlRendererContext getHtmlRendererContext() {
721         return this.rcontext;
722     }
723     
724     public UserAgentContext getUserAgentContext() {
725         return this.ucontext;
726     }
727
728     public final URL JavaDoc getFullURL(String JavaDoc uri) {
729         try {
730             String JavaDoc baseURI = this.getBaseURI();
731             URL JavaDoc documentURL = baseURI == null ? null : new URL JavaDoc(baseURI);
732             return Urls.createURL(documentURL, uri);
733         } catch(MalformedURLException JavaDoc mfu) {
734             // Try agan, without the baseURI.
735
try {
736                 return new URL JavaDoc(uri);
737             } catch(MalformedURLException JavaDoc mfu2) {
738                 logger.log(Level.WARNING,"Unable to create URL for URI=[" + uri + "], with base=[" + this.getBaseURI() + "].", mfu);
739                 return null;
740             }
741         }
742     }
743     
744     public final Location getLocation() {
745         return this.window.getLocation();
746     }
747     
748     public void setLocation(String JavaDoc location) {
749         this.getLocation().setHref(location);
750     }
751     
752     public String JavaDoc getURL() {
753         return this.documentURI;
754     }
755     
756     private HTMLElement body;
757
758     public void setBody(HTMLElement body) {
759         synchronized(this) {
760             this.body = body;
761         }
762     }
763
764     private final Collection styleSheets = new LinkedList();
765     
766     final void addStyleSheet(CSSStyleSheet ss) {
767         synchronized(this.treeLock) {
768             this.styleSheets.add(ss);
769             this.styleSheetAggregator = null;
770             // Need to invalidate all children up to
771
// this point.
772
this.forgetRenderState();
773             //TODO: this might be ineffcient.
774
ArrayList nl = this.nodeList;
775             if(nl != null) {
776                 Iterator i = nl.iterator();
777                 while(i.hasNext()) {
778                     Object JavaDoc node = i.next();
779                     if(node instanceof HTMLElementImpl) {
780                         ((HTMLElementImpl) node).forgetStyle(true);
781                     }
782                 }
783             }
784         }
785         this.allInvalidated();
786     }
787     
788     private StyleSheetAggregator styleSheetAggregator = null;
789     
790     final StyleSheetAggregator getStyleSheetAggregator() {
791         synchronized(this.treeLock) {
792             StyleSheetAggregator ssa = this.styleSheetAggregator;
793             if(ssa == null) {
794                 ssa = new StyleSheetAggregator(this);
795                 try {
796                     ssa.addStyleSheets(this.styleSheets);
797                 } catch(MalformedURLException JavaDoc mfu) {
798                     logger.log(Level.WARNING,"getStyleSheetAggregator()", mfu);
799                 }
800                 this.styleSheetAggregator = ssa;
801             }
802             return ssa;
803         }
804     }
805
806     private final ArrayList documentNotificationListeners = new ArrayList(1);
807     
808     public void addDocumentNotificationListener(DocumentNotificationListener listener) {
809         ArrayList listenersList = this.documentNotificationListeners;
810         synchronized(listenersList) {
811             listenersList.add(listener);
812         }
813     }
814
815     public void removeDocumentNotificationListener(DocumentNotificationListener listener) {
816         ArrayList listenersList = this.documentNotificationListeners;
817         synchronized(listenersList) {
818             listenersList.remove(listener);
819         }
820     }
821
822     public void sizeInvalidated(NodeImpl node) {
823         ArrayList listenersList = this.documentNotificationListeners;
824         int size;
825         synchronized(listenersList) {
826             size = listenersList.size();
827         }
828         // Traverse list outside synchronized block.
829
// (Shouldn't call listener methods in synchronized block.
830
// Deadlock is possible). But assume list could have
831
// been changed.
832
for(int i = 0; i < size; i++) {
833             try {
834                 DocumentNotificationListener dnl = (DocumentNotificationListener) listenersList.get(i);
835                 dnl.sizeInvalidated(node);
836             } catch(IndexOutOfBoundsException JavaDoc iob) {
837                 // ignore
838
}
839         }
840     }
841     
842     /**
843      * Called if something such as a color or
844      * decoration has changed. This would be
845      * something which does not affect the
846      * rendered size.
847      * @param node
848      */

849     public void lookInvalidated(NodeImpl node) {
850         ArrayList listenersList = this.documentNotificationListeners;
851         int size;
852         synchronized(listenersList) {
853             size = listenersList.size();
854         }
855         // Traverse list outside synchronized block.
856
// (Shouldn't call listener methods in synchronized block.
857
// Deadlock is possible). But assume list could have
858
// been changed.
859
for(int i = 0; i < size; i++) {
860             try {
861                 DocumentNotificationListener dnl = (DocumentNotificationListener) listenersList.get(i);
862                 dnl.lookInvalidated(node);
863             } catch(IndexOutOfBoundsException JavaDoc iob) {
864                 // ignore
865
}
866         }
867
868     }
869     
870     /**
871      * Changed if the position of the node in a
872      * parent has changed.
873      * @param node
874      */

875     public void positionInParentInvalidated(NodeImpl node) {
876         ArrayList listenersList = this.documentNotificationListeners;
877         int size;
878         synchronized(listenersList) {
879             size = listenersList.size();
880         }
881         // Traverse list outside synchronized block.
882
// (Shouldn't call listener methods in synchronized block.
883
// Deadlock is possible). But assume list could have
884
// been changed.
885
for(int i = 0; i < size; i++) {
886             try {
887                 DocumentNotificationListener dnl = (DocumentNotificationListener) listenersList.get(i);
888                 dnl.positionInvalidated(node);
889             } catch(IndexOutOfBoundsException JavaDoc iob) {
890                 // ignore
891
}
892         }
893     }
894     
895     /**
896      * This is called when the node has changed, but
897      * it is unclear if it's a size change or a look
898      * change.
899      * @param node
900      */

901     public void invalidated(NodeImpl node) {
902         ArrayList listenersList = this.documentNotificationListeners;
903         int size;
904         synchronized(listenersList) {
905             size = listenersList.size();
906         }
907         // Traverse list outside synchronized block.
908
// (Shouldn't call listener methods in synchronized block.
909
// Deadlock is possible). But assume list could have
910
// been changed.
911
for(int i = 0; i < size; i++) {
912             try {
913                 DocumentNotificationListener dnl = (DocumentNotificationListener) listenersList.get(i);
914                 dnl.invalidated(node);
915             } catch(IndexOutOfBoundsException JavaDoc iob) {
916                 // ignore
917
}
918         }
919     }
920     
921     public void nodeLoaded(NodeImpl node) {
922         ArrayList listenersList = this.documentNotificationListeners;
923         int size;
924         synchronized(listenersList) {
925             size = listenersList.size();
926         }
927         // Traverse list outside synchronized block.
928
// (Shouldn't call listener methods in synchronized block.
929
// Deadlock is possible). But assume list could have
930
// been changed.
931
for(int i = 0; i < size; i++) {
932             try {
933                 DocumentNotificationListener dnl = (DocumentNotificationListener) listenersList.get(i);
934                 dnl.nodeLoaded(node);
935             } catch(IndexOutOfBoundsException JavaDoc iob) {
936                 // ignore
937
}
938         }
939     }
940
941     public void externalScriptLoading(NodeImpl node) {
942         ArrayList listenersList = this.documentNotificationListeners;
943         int size;
944         synchronized(listenersList) {
945             size = listenersList.size();
946         }
947         // Traverse list outside synchronized block.
948
// (Shouldn't call listener methods in synchronized block.
949
// Deadlock is possible). But assume list could have
950
// been changed.
951
for(int i = 0; i < size; i++) {
952             try {
953                 DocumentNotificationListener dnl = (DocumentNotificationListener) listenersList.get(i);
954                 dnl.externalScriptLoading(node);
955             } catch(IndexOutOfBoundsException JavaDoc iob) {
956                 // ignore
957
}
958         }
959     }
960
961     public void allInvalidated() {
962         ArrayList listenersList = this.documentNotificationListeners;
963         int size;
964         synchronized(listenersList) {
965             size = listenersList.size();
966         }
967         // Traverse list outside synchronized block.
968
// (Shouldn't call listener methods in synchronized block.
969
// Deadlock is possible). But assume list could have
970
// been changed.
971
for(int i = 0; i < size; i++) {
972             try {
973                 DocumentNotificationListener dnl = (DocumentNotificationListener) listenersList.get(i);
974                 dnl.allInvalidated();
975             } catch(IndexOutOfBoundsException JavaDoc iob) {
976                 // ignore
977
}
978         }
979     }
980     
981     protected RenderState createRenderState(RenderState prevRenderState) {
982         return new StyleSheetRenderState(null, null);
983     }
984
985     private final Map imageInfos = new HashMap(4);
986     private final ImageEvent BLANK_IMAGE_EVENT = new ImageEvent(this, null);
987     
988     /**
989      * Loads images such that they are shared if from the same URI.
990      * Informs listener immediately if an image is already known.
991      * @param relativeUri
992      * @param imageListener
993      */

994     void loadImage(String JavaDoc relativeUri, ImageListener imageListener) {
995         HtmlRendererContext rcontext = this.getHtmlRendererContext();
996         if(rcontext == null) {
997             // Ignore image loading when there's no renderer context.
998
imageListener.imageLoaded(BLANK_IMAGE_EVENT);
999             return;
1000        }
1001        final URL JavaDoc url = this.getFullURL(relativeUri);
1002        if(url == null) {
1003            imageListener.imageLoaded(BLANK_IMAGE_EVENT);
1004            return;
1005        }
1006        final String JavaDoc urlText = url.toExternalForm();
1007        final Map map = this.imageInfos;
1008        ImageEvent event = null;
1009        synchronized(map) {
1010            ImageInfo info = (ImageInfo) map.get(urlText);
1011            if(info != null) {
1012                if(info.loaded) {
1013                    // TODO: This can't really happen because ImageInfo
1014
// is removed right after image is loaded.
1015
event = info.imageEvent;
1016                }
1017                else {
1018                    info.addListener(imageListener);
1019                }
1020            }
1021            else {
1022                UserAgentContext uac = rcontext.getUserAgentContext();
1023                final HttpRequest httpRequest = uac.createHttpRequest();
1024                final ImageInfo newInfo = new ImageInfo();
1025                map.put(urlText, newInfo);
1026                newInfo.addListener(imageListener);
1027                httpRequest.addReadyStateChangeListener(new ReadyStateChangeListener() {
1028                    public void readyStateChanged() {
1029                        if(httpRequest.getReadyState() == HttpRequest.STATE_COMPLETE) {
1030                            ImageEvent newEvent = new ImageEvent(HTMLDocumentImpl.this, httpRequest.getResponseImage());
1031                            ImageListener[] listeners;
1032                            synchronized(map) {
1033                                newInfo.imageEvent = newEvent;
1034                                newInfo.loaded = true;
1035                                listeners = newInfo.getListeners();
1036                                // Must remove from map in the locked block
1037
// that got the listeners. Otherwise a new
1038
// listener might miss the event??
1039
map.remove(urlText);
1040                            }
1041                            int llength = listeners.length;
1042                            for(int i = 0; i < llength; i++) {
1043                                // Call holding no locks
1044
listeners[i].imageLoaded(newEvent);
1045                            }
1046                        }
1047                    }
1048                });
1049                SecurityManager JavaDoc sm = System.getSecurityManager();
1050                if(sm == null) {
1051                    httpRequest.open("GET", url, true);
1052                }
1053                else {
1054                    AccessController.doPrivileged(new PrivilegedAction() {
1055                        public Object JavaDoc run() {
1056                            // Code might have restrictions on accessing
1057
// items from elsewhere.
1058
httpRequest.open("GET", url, true);
1059                            return null;
1060                        }
1061                    });
1062                }
1063            }
1064        }
1065        if(event != null) {
1066            // Call holding no locks.
1067
imageListener.imageLoaded(event);
1068        }
1069    }
1070    
1071    private Function onloadHandler;
1072
1073    public Function getOnloadHandler() {
1074        return onloadHandler;
1075    }
1076
1077    public void setOnloadHandler(Function onloadHandler) {
1078        this.onloadHandler = onloadHandler;
1079    }
1080
1081    public Object JavaDoc setUserData(String JavaDoc key, Object JavaDoc data, UserDataHandler handler) {
1082        Function onloadHandler = this.onloadHandler;
1083        if(onloadHandler != null) {
1084            if(org.lobobrowser.html.parser.HtmlParser.MODIFYING_KEY.equals(key) && data == Boolean.FALSE) {
1085                //TODO: onload event object?
1086
Executor.executeFunction(this, onloadHandler, null);
1087            }
1088        }
1089        return super.setUserData(key, data, handler);
1090    }
1091
1092    protected Node createSimilarNode() {
1093        return new HTMLDocumentImpl(this.ucontext, this.rcontext, this.reader, this.documentURI);
1094    }
1095
1096    private static class ImageInfo {
1097        // Access to this class is synchronized on imageInfos.
1098
public ImageEvent imageEvent;
1099        public boolean loaded;
1100        private ArrayList listeners = new ArrayList(1);
1101        
1102        void addListener(ImageListener listener) {
1103            this.listeners.add(listener);
1104        }
1105        
1106        ImageListener[] getListeners() {
1107            return (ImageListener[]) this.listeners.toArray(ImageListener.EMPTY_ARRAY);
1108        }
1109    }
1110    
1111    private class ImageFilter implements NodeFilter {
1112        public boolean accept(Node node) {
1113            return "IMG".equalsIgnoreCase(node.getNodeName());
1114        }
1115    }
1116
1117    private class AppletFilter implements NodeFilter {
1118        public boolean accept(Node node) {
1119            //TODO: "OBJECT" elements that are applets too.
1120
return "APPLET".equalsIgnoreCase(node.getNodeName());
1121        }
1122    }
1123
1124    private class LinkFilter implements NodeFilter {
1125        public boolean accept(Node node) {
1126            String JavaDoc nodeName = node.getNodeName();
1127            return "LINK".equalsIgnoreCase(nodeName);
1128        }
1129    }
1130
1131    private class AnchorFilter implements NodeFilter {
1132        public boolean accept(Node node) {
1133            String JavaDoc nodeName = node.getNodeName();
1134            return "A".equalsIgnoreCase(nodeName) || "ANCHOR".equalsIgnoreCase(nodeName);
1135        }
1136    }
1137    
1138    private class FormFilter implements NodeFilter {
1139        public boolean accept(Node node) {
1140            String JavaDoc nodeName = node.getNodeName();
1141            return "FORM".equalsIgnoreCase(nodeName);
1142        }
1143    }
1144
1145    private class FrameFilter implements NodeFilter {
1146        public boolean accept(Node node) {
1147            return node instanceof org.w3c.dom.html2.HTMLFrameElement ||
1148                   node instanceof org.w3c.dom.html2.HTMLIFrameElement;
1149        }
1150    }
1151
1152// private class BodyFilter implements NodeFilter {
1153
// public boolean accept(Node node) {
1154
// return node instanceof org.w3c.dom.html2.HTMLBodyElement;
1155
// }
1156
// }
1157

1158    private class ElementNameFilter implements NodeFilter {
1159        private final String JavaDoc name;
1160        
1161        public ElementNameFilter(String JavaDoc name) {
1162            this.name = name;
1163        }
1164        
1165        public boolean accept(Node node) {
1166            //TODO: Case sensitive?
1167
return (node instanceof Element) &&
1168                this.name.equals(((Element) node).getAttribute("name"));
1169        }
1170    }
1171    
1172    private class ElementFilter implements NodeFilter {
1173        public ElementFilter() {
1174        }
1175        
1176        public boolean accept(Node node) {
1177            return node instanceof Element;
1178        }
1179    }
1180
1181    private class TagNameFilter implements NodeFilter {
1182        private final String JavaDoc name;
1183        
1184        public TagNameFilter(String JavaDoc name) {
1185            this.name = name;
1186        }
1187        
1188        public boolean accept(Node node) {
1189            if(!(node instanceof Element)) {
1190                return false;
1191            }
1192            String JavaDoc n = this.name;
1193            return n.equalsIgnoreCase(((Element) node).getTagName());
1194        }
1195    }
1196    
1197    /**
1198     * Tag class that also notifies document
1199     * when text is written to an open buffer.
1200     * @author J. H. S.
1201     */

1202    private class LocalWritableLineReader extends WritableLineReader {
1203        /**
1204         * @param reader
1205         */

1206        public LocalWritableLineReader(LineNumberReader reader) {
1207            super(reader);
1208        }
1209
1210        /**
1211         * @param reader
1212         */

1213        public LocalWritableLineReader(Reader reader) {
1214            super(reader);
1215        }
1216
1217        public void write(String JavaDoc text) throws IOException {
1218            super.write(text);
1219            if("".equals(text)) {
1220                openBufferChanged(text);
1221            }
1222        }
1223    }
1224}
1225
Popular Tags