Back to index

lightning-sunbird  0.9+nobinonly
nsXMLElement.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator client code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsXMLElement.h"
00040 #include "nsHTMLAtoms.h"
00041 #include "nsLayoutAtoms.h"
00042 #include "nsIDocument.h"
00043 #include "nsIAtom.h"
00044 #include "nsNetUtil.h"
00045 #include "nsIEventListenerManager.h"
00046 #include "nsIDocShell.h"
00047 #include "nsIEventStateManager.h"
00048 #include "nsIDOMEvent.h"
00049 #include "nsINameSpaceManager.h"
00050 #include "nsINodeInfo.h"
00051 #include "nsIURL.h"
00052 #include "nsIIOService.h"
00053 #include "nsNetCID.h"
00054 #include "nsIServiceManager.h"
00055 #include "nsXPIDLString.h"
00056 #include "nsIDocShell.h"
00057 #include "nsIDocShellTreeItem.h"
00058 #include "nsIScriptSecurityManager.h"
00059 #include "nsIRefreshURI.h"
00060 #include "nsStyleConsts.h"
00061 #include "nsIPresShell.h"
00062 #include "nsGUIEvent.h"
00063 #include "nsPresContext.h"
00064 #include "nsIDOMCSSStyleDeclaration.h"
00065 #include "nsIDOMViewCSS.h"
00066 #include "nsIXBLService.h"
00067 #include "nsIBindingManager.h"
00068 
00069 nsresult
00070 NS_NewXMLElement(nsIContent** aInstancePtrResult, nsINodeInfo *aNodeInfo)
00071 {
00072   nsXMLElement* it = new nsXMLElement(aNodeInfo);
00073   if (!it) {
00074     return NS_ERROR_OUT_OF_MEMORY;
00075   }
00076 
00077   NS_ADDREF(*aInstancePtrResult = it);
00078 
00079   return NS_OK;
00080 }
00081 
00082 nsXMLElement::nsXMLElement(nsINodeInfo *aNodeInfo)
00083   : nsGenericElement(aNodeInfo),
00084     mIsLink(PR_FALSE)
00085 {
00086 }
00087 
00088 nsXMLElement::~nsXMLElement()
00089 {
00090 }
00091 
00092 
00093 // QueryInterface implementation for nsXMLElement
00094 NS_IMETHODIMP 
00095 nsXMLElement::QueryInterface(REFNSIID aIID, void** aInstancePtr)
00096 {
00097   NS_ENSURE_ARG_POINTER(aInstancePtr);
00098   *aInstancePtr = nsnull;
00099 
00100   nsresult rv = nsGenericElement::QueryInterface(aIID, aInstancePtr);
00101 
00102   if (NS_SUCCEEDED(rv))
00103     return rv;
00104 
00105   nsISupports *inst = nsnull;
00106 
00107   if (aIID.Equals(NS_GET_IID(nsIDOMNode))) {
00108     inst = NS_STATIC_CAST(nsIDOMNode *, this);
00109   } else if (aIID.Equals(NS_GET_IID(nsIDOMElement))) {
00110     inst = NS_STATIC_CAST(nsIDOMElement *, this);
00111   } else if (aIID.Equals(NS_GET_IID(nsIXMLContent))) {
00112     inst = NS_STATIC_CAST(nsIXMLContent *, this);
00113   } else if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
00114     inst = nsContentUtils::GetClassInfoInstance(eDOMClassInfo_Element_id);
00115     NS_ENSURE_TRUE(inst, NS_ERROR_OUT_OF_MEMORY);
00116   } else {
00117     return PostQueryInterface(aIID, aInstancePtr);
00118   }
00119 
00120   NS_ADDREF(inst);
00121 
00122   *aInstancePtr = inst;
00123 
00124   return NS_OK;
00125 }
00126 
00127 
00128 NS_IMPL_ADDREF_INHERITED(nsXMLElement, nsGenericElement)
00129 NS_IMPL_RELEASE_INHERITED(nsXMLElement, nsGenericElement)
00130 
00131 nsresult
00132 nsXMLElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, nsIAtom* aPrefix,
00133                       const nsAString& aValue, PRBool aNotify)
00134 {
00135   if (aNameSpaceID == kNameSpaceID_XLink && aName == nsHTMLAtoms::type) { 
00136 
00137     // NOTE: This really is a link according to the XLink spec,
00138     //       we do not need to check other attributes. If there
00139     //       is no href attribute, then this link is simply
00140     //       untraversible [XLink 3.2].
00141     mIsLink = aValue.EqualsLiteral("simple");
00142 
00143     // We will check for actuate="onLoad" in MaybeTriggerAutoLink
00144   }
00145 
00146   return nsGenericElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue,
00147                                    aNotify);
00148 }
00149 
00150 static nsresult
00151 DocShellToPresContext(nsIDocShell *aShell, nsPresContext **aPresContext)
00152 {
00153   *aPresContext = nsnull;
00154 
00155   nsresult rv;
00156   nsCOMPtr<nsIDocShell> ds = do_QueryInterface(aShell,&rv);
00157   if (NS_FAILED(rv))
00158     return rv;
00159 
00160   return ds->GetPresContext(aPresContext);
00161 }
00162 
00163 static inline
00164 nsresult SpecialAutoLoadReturn(nsresult aRv, nsLinkVerb aVerb)
00165 {
00166   if (NS_SUCCEEDED(aRv)) {
00167     switch(aVerb) {
00168       case eLinkVerb_Embed:
00169         aRv = NS_XML_AUTOLINK_EMBED;
00170         break;
00171       case eLinkVerb_New:
00172         aRv = NS_XML_AUTOLINK_NEW;
00173         break;
00174       case eLinkVerb_Replace:
00175         aRv = NS_XML_AUTOLINK_REPLACE;
00176         break;
00177       default:
00178         aRv = NS_XML_AUTOLINK_UNDEFINED;
00179         break;
00180     }
00181   }
00182   return aRv;
00183 }
00184 
00185 NS_IMETHODIMP
00186 nsXMLElement::MaybeTriggerAutoLink(nsIDocShell *aShell)
00187 {
00188   NS_ENSURE_ARG_POINTER(aShell);
00189 
00190   nsresult rv = NS_OK;
00191 
00192   if (mIsLink) {
00193     NS_NAMED_LITERAL_STRING(onloadString, "onLoad");
00194     do {
00195       // actuate="onLoad" ?
00196       nsAutoString value;
00197       rv = nsGenericElement::GetAttr(kNameSpaceID_XLink,
00198                                      nsLayoutAtoms::actuate, value);
00199       if (rv == NS_CONTENT_ATTR_HAS_VALUE &&
00200           value.Equals(onloadString)) {
00201 
00202         // Disable in Mail/News for now. We may want a pref to control
00203         // this at some point.
00204         nsCOMPtr<nsIDocShellTreeItem> docShellItem(do_QueryInterface(aShell));
00205         if (docShellItem) {
00206           nsCOMPtr<nsIDocShellTreeItem> rootItem;
00207           docShellItem->GetRootTreeItem(getter_AddRefs(rootItem));
00208           nsCOMPtr<nsIDocShell> docshell(do_QueryInterface(rootItem));
00209           if (docshell) {
00210             PRUint32 appType;
00211             if (NS_SUCCEEDED(docshell->GetAppType(&appType)) &&
00212                 appType == nsIDocShell::APP_TYPE_MAIL) {
00213               return NS_OK;
00214             }
00215           }
00216         }
00217 
00218         // show= ?
00219         nsLinkVerb verb = eLinkVerb_Undefined; // basically means same as replace
00220         rv = nsGenericElement::GetAttr(kNameSpaceID_XLink,
00221                                        nsLayoutAtoms::show, value);
00222         if (NS_FAILED(rv))
00223           break;
00224 
00225         // XXX Should probably do this using atoms 
00226         if (value.EqualsLiteral("new")) {
00227           // We should just act like an HTML link with target="_blank" and if
00228           // someone diverts or blocks those, that's fine with us.  We don't
00229           // care.
00230           verb = eLinkVerb_New;
00231         } else if (value.EqualsLiteral("replace")) {
00232           // We want to actually stop processing the current document now.
00233           // We do this by returning the correct value so that the one
00234           // that called us knows to stop processing.
00235           verb = eLinkVerb_Replace;
00236         } else if (value.EqualsLiteral("embed")) {
00237           // XXX TODO
00238           break;
00239         }
00240 
00241         // base
00242         nsCOMPtr<nsIURI> uri = nsContentUtils::GetXLinkURI(this);
00243         if (uri) {
00244           nsCOMPtr<nsPresContext> pc;
00245           rv = DocShellToPresContext(aShell, getter_AddRefs(pc));
00246           if (NS_SUCCEEDED(rv)) {
00247             rv = TriggerLink(pc, verb, uri, EmptyString(), PR_TRUE, PR_FALSE);
00248 
00249             return SpecialAutoLoadReturn(rv, verb);
00250           }
00251         } // href
00252       }
00253     } while (0);
00254   }
00255 
00256   return rv;
00257 }
00258 
00259 nsresult
00260 nsXMLElement::HandleDOMEvent(nsPresContext* aPresContext,
00261                              nsEvent* aEvent,
00262                              nsIDOMEvent** aDOMEvent,
00263                              PRUint32 aFlags,
00264                              nsEventStatus* aEventStatus)
00265 {
00266   NS_ENSURE_ARG_POINTER(aEventStatus);
00267   // Try script event handlers first
00268   nsresult ret = nsGenericElement::HandleDOMEvent(aPresContext, aEvent,
00269                                                   aDOMEvent, aFlags,
00270                                                   aEventStatus);
00271 
00272   if (mIsLink && (NS_OK == ret) && (nsEventStatus_eIgnore == *aEventStatus) &&
00273       !(aFlags & NS_EVENT_FLAG_CAPTURE) && !(aFlags & NS_EVENT_FLAG_SYSTEM_EVENT)) {
00274     nsIDocument *document = GetCurrentDoc();
00275     switch (aEvent->message) {
00276     case NS_MOUSE_LEFT_BUTTON_DOWN:
00277       {
00278         aPresContext->EventStateManager()->
00279           SetContentState(this, NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_FOCUS);
00280 
00281         *aEventStatus = nsEventStatus_eConsumeDoDefault;
00282       }
00283       break;
00284 
00285     case NS_MOUSE_LEFT_CLICK:
00286       {
00287         if (nsEventStatus_eConsumeNoDefault != *aEventStatus) {
00288           nsInputEvent* inputEvent = NS_STATIC_CAST(nsInputEvent*, aEvent);
00289           if (inputEvent->isControl || inputEvent->isMeta ||
00290               inputEvent->isAlt || inputEvent->isShift) {
00291             break;  // let the click go through so we can handle it in JS/XUL
00292           }
00293           nsAutoString show, href;
00294           nsLinkVerb verb = eLinkVerb_Undefined; // basically means same as replace
00295           nsCOMPtr<nsIURI> uri = nsContentUtils::GetXLinkURI(this);
00296           if (!uri) {
00297             *aEventStatus = nsEventStatus_eConsumeDoDefault; 
00298             break;
00299           }
00300 
00301           nsGenericElement::GetAttr(kNameSpaceID_XLink, nsLayoutAtoms::show,
00302                                     show);
00303 
00304           // XXX Should probably do this using atoms 
00305           if (show.EqualsLiteral("new")) {
00306             verb = eLinkVerb_New;
00307           } else if (show.EqualsLiteral("replace")) {
00308             verb = eLinkVerb_Replace;
00309           } else if (show.EqualsLiteral("embed")) {
00310             verb = eLinkVerb_Embed;
00311           }
00312 
00313           nsAutoString target;
00314           GetAttr(kNameSpaceID_XLink, nsLayoutAtoms::_moz_target, target);
00315           ret = TriggerLink(aPresContext, verb, uri,
00316                             target, PR_TRUE, PR_TRUE);
00317 
00318           *aEventStatus = nsEventStatus_eConsumeDoDefault; 
00319         }
00320       }
00321       break;
00322 
00323     case NS_MOUSE_RIGHT_BUTTON_DOWN:
00324       // XXX Bring up a contextual menu provided by the application
00325       break;
00326 
00327     case NS_KEY_PRESS:
00328       if (aEvent->eventStructType == NS_KEY_EVENT) {
00329         nsKeyEvent* keyEvent = NS_STATIC_CAST(nsKeyEvent*, aEvent);
00330         if (keyEvent->keyCode == NS_VK_RETURN) {
00331           nsEventStatus status = nsEventStatus_eIgnore;
00332 
00333           //fire click
00334           nsGUIEvent* guiEvent = NS_STATIC_CAST(nsGUIEvent*, aEvent);
00335           nsMouseEvent event(NS_IS_TRUSTED_EVENT(aEvent), NS_MOUSE_LEFT_CLICK,
00336                              guiEvent->widget, nsMouseEvent::eReal);
00337           event.point = aEvent->point;
00338           event.refPoint = aEvent->refPoint;
00339           event.clickCount = 1;
00340           event.isShift = keyEvent->isShift;
00341           event.isControl = keyEvent->isControl;
00342           event.isAlt = keyEvent->isAlt;
00343           event.isMeta = keyEvent->isMeta;
00344 
00345           nsCOMPtr<nsIPresShell> presShell = aPresContext->GetPresShell();
00346           if (presShell) {
00347             ret = presShell->HandleDOMEventWithTarget(this, &event, &status);
00348             // presShell may no longer be alive, don't use it here
00349             // unless you keep a reference.
00350           }
00351         }
00352       }
00353       break;
00354 
00355     case NS_MOUSE_ENTER_SYNTH:
00356       {
00357         nsCOMPtr<nsIURI> uri = nsContentUtils::GetXLinkURI(this);
00358         if (uri) {
00359           ret = TriggerLink(aPresContext, eLinkVerb_Replace, uri,
00360                             EmptyString(), PR_FALSE, PR_TRUE);
00361         }
00362         
00363         *aEventStatus = nsEventStatus_eConsumeDoDefault; 
00364       }
00365       break;
00366 
00367       // XXX this doesn't seem to do anything yet
00368     case NS_MOUSE_EXIT_SYNTH:
00369       {
00370         ret = LeaveLink(aPresContext);
00371         *aEventStatus = nsEventStatus_eConsumeDoDefault; 
00372       }
00373       break;
00374 
00375     default:
00376       break;
00377     }
00378   }
00379 
00380   return ret;
00381 }
00382 
00383 PRBool
00384 nsXMLElement::IsFocusable(PRInt32 *aTabIndex)
00385 {
00386   nsCOMPtr<nsIURI> linkURI = nsContentUtils::GetLinkURI(this);
00387   if (linkURI) {
00388     if (aTabIndex) {
00389       *aTabIndex = ((sTabFocusModel & eTabFocus_linksMask) == 0 ? -1 : 0);
00390     }
00391     return PR_TRUE;
00392   }
00393 
00394   if (aTabIndex) {
00395     *aTabIndex = -1;
00396   }
00397 
00398   return PR_FALSE;
00399 }
00400 
00401 
00402 NS_IMPL_DOM_CLONENODE(nsXMLElement)
00403