Back to index

lightning-sunbird  0.9+nobinonly
nsHTMLAnonymousUtils.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is Mozilla.org.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corp.
00018  * Portions created by the Initial Developer are Copyright (C) 2003
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *   Daniel Glazman (glazman@netscape.com) (Original author)
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsHTMLEditor.h"
00039 
00040 #include "nsIContent.h"
00041 #include "nsIDocument.h"
00042 #include "nsIEditor.h"
00043 #include "nsIPresShell.h"
00044 #include "nsPresContext.h"
00045 
00046 #include "nsISelection.h"
00047 
00048 #include "nsTextEditUtils.h"
00049 #include "nsEditorUtils.h"
00050 #include "nsHTMLEditUtils.h"
00051 #include "nsTextEditRules.h"
00052 
00053 #include "nsIDOMHTMLElement.h"
00054 #include "nsIDOMNSHTMLElement.h"
00055 #include "nsIDOMEventTarget.h"
00056 
00057 #include "nsIDOMCSSValue.h"
00058 #include "nsIDOMCSSPrimitiveValue.h"
00059 #include "nsIDOMCSSStyleDeclaration.h"
00060 
00061 #include "nsUnicharUtils.h"
00062 
00063 // retrieve an integer stored into a CSS computed float value
00064 static PRInt32 GetCSSFloatValue(nsIDOMCSSStyleDeclaration * aDecl,
00065                                 const nsAString & aProperty)
00066 {
00067   NS_ENSURE_ARG_POINTER(aDecl);
00068 
00069   nsCOMPtr<nsIDOMCSSValue> value;
00070   // get the computed CSSValue of the property
00071   nsresult res = aDecl->GetPropertyCSSValue(aProperty, getter_AddRefs(value));
00072   if (NS_FAILED(res) || !value) return 0;
00073 
00074   // check the type of the returned CSSValue; we handle here only
00075   // pixel and enum types
00076   nsCOMPtr<nsIDOMCSSPrimitiveValue> val = do_QueryInterface(value);
00077   PRUint16 type;
00078   val->GetPrimitiveType(&type);
00079 
00080   float f;
00081   switch (type) {
00082     case nsIDOMCSSPrimitiveValue::CSS_PX:
00083       // the value is in pixels, just get it
00084       res = val->GetFloatValue(nsIDOMCSSPrimitiveValue::CSS_PX, &f);
00085       if (NS_FAILED(res)) return 0;
00086       break;
00087     case nsIDOMCSSPrimitiveValue::CSS_IDENT: {
00088       // the value is keyword, we have to map these keywords into
00089       // numeric values
00090       nsAutoString str;
00091       res = val->GetStringValue(str);
00092       if (str.EqualsLiteral("thin"))
00093         f = 1;
00094       if (str.EqualsLiteral("medium"))
00095         f = 3;
00096       if (str.EqualsLiteral("thick"))
00097         f = 5;
00098       break;
00099     }
00100     default:
00101       f = 0;
00102   }
00103 
00104   return (PRInt32) f;
00105 }
00106 
00107 // Returns in *aReturn an anonymous nsDOMElement of type aTag,
00108 // child of aParentNode. If aIsCreatedHidden is true, the class
00109 // "hidden" is added to the created element. If aAnonClass is not
00110 // the empty string, it becomes the value of the attribute "_moz_anonclass"
00111 nsresult
00112 nsHTMLEditor::CreateAnonymousElement(const nsAString & aTag, nsIDOMNode *  aParentNode,
00113                                      const nsAString & aAnonClass, PRBool aIsCreatedHidden,
00114                                      nsIDOMElement ** aReturn)
00115 {
00116   NS_ENSURE_ARG_POINTER(aParentNode);
00117   NS_ENSURE_ARG_POINTER(aReturn);
00118 
00119   nsCOMPtr<nsIContent> parentContent( do_QueryInterface(aParentNode) );
00120   if (!parentContent)
00121     return NS_OK;
00122 
00123   // Get the document
00124   nsCOMPtr<nsIDOMDocument> domDoc;
00125   GetDocument(getter_AddRefs(domDoc));
00126   nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
00127   if (!doc) return NS_ERROR_NULL_POINTER;
00128 
00129   // Get the pres shell
00130   nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
00131   if (!ps) return NS_ERROR_NOT_INITIALIZED;
00132 
00133   // Create a new node through the element factory
00134   nsCOMPtr<nsIContent> newContent;
00135   nsresult res = CreateHTMLContent(aTag, getter_AddRefs(newContent));
00136   if (NS_FAILED(res)) return res;
00137 
00138   nsCOMPtr<nsIDOMElement> newElement = do_QueryInterface(newContent);
00139   if (!newElement)
00140     return NS_ERROR_FAILURE;
00141 
00142   // add the "hidden" class if needed
00143   if (aIsCreatedHidden) {
00144     res = newElement->SetAttribute(NS_LITERAL_STRING("class"),
00145                                    NS_LITERAL_STRING("hidden"));
00146     if (NS_FAILED(res)) return res;
00147   }
00148 
00149   // add an _moz_anonclass attribute if needed
00150   if (!aAnonClass.IsEmpty()) {
00151     res = newElement->SetAttribute(NS_LITERAL_STRING("_moz_anonclass"),
00152                                    aAnonClass);
00153     if (NS_FAILED(res)) return res;
00154   }
00155 
00156   // establish parenthood of the element
00157   newContent->SetNativeAnonymous(PR_TRUE);
00158   res = newContent->BindToTree(doc, parentContent, newContent, PR_TRUE);
00159   if (NS_FAILED(res)) {
00160     newContent->UnbindFromTree();
00161     return res;
00162   }
00163   
00164   // display the element
00165   ps->RecreateFramesFor(newContent);
00166 
00167   *aReturn = newElement;
00168   NS_IF_ADDREF(*aReturn);
00169   return NS_OK;
00170 }
00171 
00172 // Removes event listener and calls DeleteRefToAnonymousNode.
00173 void
00174 nsHTMLEditor::RemoveListenerAndDeleteRef(const nsAString& aEvent,
00175                                          nsIDOMEventListener* aListener,
00176                                          PRBool aUseCapture,
00177                                          nsIDOMElement* aElement,
00178                                          nsIContent * aParentContent,
00179                                          nsIPresShell* aShell)
00180 {
00181   nsCOMPtr<nsIDOMEventTarget> evtTarget(do_QueryInterface(aElement));
00182   if (evtTarget) {
00183     evtTarget->RemoveEventListener(aEvent, aListener, aUseCapture);
00184   }
00185   DeleteRefToAnonymousNode(aElement, aParentContent, aShell);
00186 }
00187 
00188 // Deletes all references to an anonymous element
00189 void
00190 nsHTMLEditor::DeleteRefToAnonymousNode(nsIDOMElement* aElement,
00191                                        nsIContent* aParentContent,
00192                                        nsIPresShell* aShell)
00193 {
00194   // call ContentRemoved() for the anonymous content
00195   // node so its references get removed from the frame manager's
00196   // undisplay map, and its layout frames get destroyed!
00197 
00198   if (aElement) {
00199     nsCOMPtr<nsIContent> content = do_QueryInterface(aElement);
00200     if (content) {
00201       // Need to check whether aShell has been destroyed (but not yet deleted).
00202       // In that case presContext->GetPresShell() returns nsnull.
00203       // See bug 338129.
00204       if (aShell && aShell->GetPresContext() &&
00205           aShell->GetPresContext()->GetPresShell() == aShell) {
00206         nsCOMPtr<nsIDocumentObserver> docObserver = do_QueryInterface(aShell);
00207         if (docObserver) {
00208           docObserver->ContentRemoved(content->GetCurrentDoc(),
00209                                       aParentContent, content, -1);
00210         }
00211       }
00212       content->UnbindFromTree();
00213     }
00214   }
00215 }  
00216 
00217 // The following method is mostly called by a selection listener. When a
00218 // selection change is notified, the method is called to check if resizing
00219 // handles, a grabber and/or inline table editing UI need to be displayed
00220 // or refreshed
00221 NS_IMETHODIMP
00222 nsHTMLEditor::CheckSelectionStateForAnonymousButtons(nsISelection * aSelection)
00223 {
00224   NS_ENSURE_ARG_POINTER(aSelection);
00225 
00226   // early way out if all contextual UI extensions are disabled
00227   if (!mIsObjectResizingEnabled &&
00228       !mIsAbsolutelyPositioningEnabled &&
00229       !mIsInlineTableEditingEnabled)
00230     return NS_OK;
00231 
00232   nsCOMPtr<nsIDOMElement> focusElement;
00233   // let's get the containing element of the selection
00234   nsresult res  = GetSelectionContainer(getter_AddRefs(focusElement));
00235   if (!focusElement) return NS_OK;
00236   if (NS_FAILED(res)) return res;
00237 
00238   // what's its tag?
00239   nsAutoString focusTagName;
00240   res = focusElement->GetTagName(focusTagName);
00241   if (NS_FAILED(res)) return res;
00242   ToLowerCase(focusTagName);
00243   nsCOMPtr<nsIAtom> focusTagAtom = do_GetAtom(focusTagName);
00244 
00245   nsCOMPtr<nsIDOMElement> absPosElement;
00246   if (mIsAbsolutelyPositioningEnabled) {
00247     // Absolute Positioning support is enabled, is the selection contained
00248     // in an absolutely positioned element ?
00249     res = GetAbsolutelyPositionedSelectionContainer(getter_AddRefs(absPosElement));
00250     if (NS_FAILED(res)) return res;
00251   }
00252 
00253   nsCOMPtr<nsIDOMElement> cellElement;
00254   if (mIsObjectResizingEnabled || mIsInlineTableEditingEnabled) {
00255     // Resizing or Inline Table Editing is enabled, we need to check if the
00256     // selection is contained in a table cell
00257     res = GetElementOrParentByTagName(NS_LITERAL_STRING("td"),
00258                                       nsnull,
00259                                       getter_AddRefs(cellElement));
00260     if (NS_FAILED(res)) return res;
00261   }
00262 
00263   if (mIsObjectResizingEnabled && cellElement) {
00264     // we are here because Resizing is enabled AND selection is contained in
00265     // a cell
00266 
00267     // get the enclosing table
00268     if (nsEditProperty::img != focusTagAtom) {
00269       // the element container of the selection is not an image, so we'll show
00270       // the resizers around the table
00271       nsCOMPtr<nsIDOMNode> tableNode = GetEnclosingTable(cellElement);
00272       focusElement = do_QueryInterface(tableNode);
00273       focusTagAtom = nsEditProperty::table;
00274     }
00275   }
00276 
00277   // we allow resizers only around images, tables, and absolutely positioned
00278   // elements. If we don't have image/table, let's look at the latter case.
00279   if (nsEditProperty::img != focusTagAtom &&
00280       nsEditProperty::table != focusTagAtom)
00281     focusElement = absPosElement;
00282 
00283   // at this point, focusElement  contains the element for Resizing,
00284   //                cellElement   contains the element for InlineTableEditing
00285   //                absPosElement contains the element for Positioning
00286 
00287   // first let's cancel old settings if needed
00288   PRBool refreshResizing     = (mResizedObject != nsnull);
00289   PRBool refreshPositioning  = (mAbsolutelyPositionedObject != nsnull);
00290   PRBool refreshTableEditing = (mInlineEditedCell != nsnull);
00291 
00292   if (mIsAbsolutelyPositioningEnabled && mAbsolutelyPositionedObject &&
00293       absPosElement != mAbsolutelyPositionedObject) {
00294     res = HideGrabber();
00295     if (NS_FAILED(res)) return res;
00296     refreshPositioning = PR_FALSE;
00297   }
00298 
00299   if (mIsObjectResizingEnabled && mResizedObject &&
00300       mResizedObject != focusElement) {
00301     res = HideResizers();
00302     if (NS_FAILED(res)) return res;
00303     refreshResizing = PR_FALSE;
00304   }
00305 
00306   if (mIsInlineTableEditingEnabled && mInlineEditedCell &&
00307       mInlineEditedCell != cellElement) {
00308     res = HideInlineTableEditingUI();
00309     if (NS_FAILED(res)) return res;
00310     refreshTableEditing = PR_FALSE;
00311   }
00312 
00313   // now, let's display all contextual UI for good
00314 
00315   if (mIsObjectResizingEnabled && focusElement) {
00316     if (nsEditProperty::img == focusTagAtom)
00317       mResizedObjectIsAnImage = PR_TRUE;
00318     if (refreshResizing)
00319       res = RefreshResizers();
00320     else
00321       res = ShowResizers(focusElement);
00322     if (NS_FAILED(res)) return res;
00323   }
00324 
00325   if (mIsAbsolutelyPositioningEnabled && absPosElement) {
00326     if (refreshPositioning)
00327       res = RefreshGrabber();
00328     else
00329       res = ShowGrabberOnElement(absPosElement);
00330     if (NS_FAILED(res)) return res;
00331   }
00332 
00333   if (mIsInlineTableEditingEnabled && cellElement) {
00334     if (refreshTableEditing)
00335       res = RefreshInlineTableEditingUI();
00336     else
00337       res = ShowInlineTableEditingUI(cellElement);
00338   }
00339 
00340   return res;
00341 }
00342 
00343 // Resizing and Absolute Positioning need to know everything about the
00344 // containing box of the element: position, size, margins, borders
00345 nsresult
00346 nsHTMLEditor::GetPositionAndDimensions(nsIDOMElement * aElement,
00347                                        PRInt32 & aX, PRInt32 & aY,
00348                                        PRInt32 & aW, PRInt32 & aH,
00349                                        PRInt32 & aBorderLeft,
00350                                        PRInt32 & aBorderTop,
00351                                        PRInt32 & aMarginLeft,
00352                                        PRInt32 & aMarginTop)
00353 {
00354   NS_ENSURE_ARG_POINTER(aElement);
00355 
00356   // Is the element positioned ? let's check the cheap way first...
00357   PRBool isPositioned = PR_FALSE;
00358   nsresult res = aElement->HasAttribute(NS_LITERAL_STRING("_moz_abspos"), &isPositioned);
00359   if (NS_FAILED(res)) return res;
00360   if (!isPositioned) {
00361     // hmmm... the expensive way now...
00362     nsAutoString positionStr;
00363     mHTMLCSSUtils->GetComputedProperty(aElement, nsEditProperty::cssPosition,
00364                                        positionStr);
00365     isPositioned = positionStr.EqualsLiteral("absolute");
00366   }
00367 
00368   if (isPositioned) {
00369     // Yes, it is absolutely positioned
00370     mResizedObjectIsAbsolutelyPositioned = PR_TRUE;
00371 
00372     nsCOMPtr<nsIDOMViewCSS> viewCSS;
00373     res = mHTMLCSSUtils->GetDefaultViewCSS(aElement, getter_AddRefs(viewCSS));
00374     if (NS_FAILED(res)) return res;
00375 
00376     nsAutoString empty;
00377     nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl;
00378     // Get the all the computed css styles attached to the element node
00379     res = viewCSS->GetComputedStyle(aElement, empty, getter_AddRefs(cssDecl));
00380     if (NS_FAILED(res)) return res;
00381 
00382     aBorderLeft = GetCSSFloatValue(cssDecl, NS_LITERAL_STRING("border-left-width"));
00383     aBorderTop  = GetCSSFloatValue(cssDecl, NS_LITERAL_STRING("border-top-width"));
00384     aMarginLeft = GetCSSFloatValue(cssDecl, NS_LITERAL_STRING("margin-left"));
00385     aMarginTop  = GetCSSFloatValue(cssDecl, NS_LITERAL_STRING("margin-top"));
00386 
00387     aX = GetCSSFloatValue(cssDecl, NS_LITERAL_STRING("left")) +
00388          aMarginLeft + aBorderLeft;
00389     aY = GetCSSFloatValue(cssDecl, NS_LITERAL_STRING("top")) +
00390          aMarginTop + aBorderTop;
00391     aW = GetCSSFloatValue(cssDecl, NS_LITERAL_STRING("width"));
00392     aH = GetCSSFloatValue(cssDecl, NS_LITERAL_STRING("height"));
00393   }
00394   else {
00395     mResizedObjectIsAbsolutelyPositioned = PR_FALSE;
00396     nsCOMPtr<nsIDOMNSHTMLElement> nsElement = do_QueryInterface(aElement);
00397     if (!nsElement) {return NS_ERROR_NULL_POINTER; }
00398 
00399     GetElementOrigin(aElement, aX, aY);
00400 
00401     res = nsElement->GetOffsetWidth(&aW);
00402     if (NS_FAILED(res)) return res;
00403     res = nsElement->GetOffsetHeight(&aH);
00404 
00405     aBorderLeft = 0;
00406     aBorderTop  = 0;
00407     aMarginLeft = 0;
00408     aMarginTop = 0;
00409   }
00410   return res;
00411 }
00412 
00413 // self-explanatory
00414 void
00415 nsHTMLEditor::SetAnonymousElementPosition(PRInt32 aX, PRInt32 aY, nsIDOMElement *aElement)
00416 {
00417   mHTMLCSSUtils->SetCSSPropertyPixels(aElement, NS_LITERAL_STRING("left"), aX);
00418   mHTMLCSSUtils->SetCSSPropertyPixels(aElement, NS_LITERAL_STRING("top"), aY);
00419 }