Back to index

lightning-sunbird  0.9+nobinonly
nsBoxObject.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  *   Original Author: David W. Hyatt (hyatt@netscape.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 "nsBoxObject.h"
00040 #include "nsIBoxLayoutManager.h"
00041 #include "nsIBoxPaintManager.h"
00042 #include "nsIDocument.h"
00043 #include "nsIPresShell.h"
00044 #include "nsPresContext.h"
00045 #include "nsIDocument.h"
00046 #include "nsIContent.h"
00047 #include "nsIFrame.h"
00048 #include "nsIFrameFrame.h"
00049 #include "nsIDocShell.h"
00050 #include "nsReadableUtils.h"
00051 #include "nsILookAndFeel.h"
00052 #include "nsWidgetsCID.h"
00053 #include "nsIServiceManager.h"
00054 #include "nsIDOMClassInfo.h"
00055 #include "nsIView.h"
00056 #include "nsIWidget.h"
00057 #include "nsIDOMXULElement.h"
00058 #include "nsIFrame.h"
00059 
00060 // Static IIDs/CIDs. Try to minimize these.
00061 static NS_DEFINE_CID(kLookAndFeelCID, NS_LOOKANDFEEL_CID);
00062 
00063 // Implementation /////////////////////////////////////////////////////////////////
00064 
00065 // Static member variable initialization
00066 
00067 // Implement our nsISupports methods
00068 
00069 // QueryInterface implementation for nsBoxObject
00070 NS_INTERFACE_MAP_BEGIN(nsBoxObject)
00071   NS_INTERFACE_MAP_ENTRY(nsIBoxObject)
00072   NS_INTERFACE_MAP_ENTRY(nsPIBoxObject)
00073   NS_INTERFACE_MAP_ENTRY(nsISupports)
00074   NS_INTERFACE_MAP_ENTRY_DOM_CLASSINFO(BoxObject)
00075 NS_INTERFACE_MAP_END
00076 
00077 
00078 NS_IMPL_ADDREF(nsBoxObject)
00079 NS_IMPL_RELEASE(nsBoxObject)
00080 
00081 
00082 // Constructors/Destructors
00083 nsBoxObject::nsBoxObject(void)
00084   :mContent(nsnull), mPresShell(nsnull)
00085 {
00086 }
00087 
00088 nsBoxObject::~nsBoxObject(void)
00089 {
00090 }
00091 
00092 NS_IMETHODIMP
00093 nsBoxObject::GetElement(nsIDOMElement** aResult)
00094 {
00095   if (mContent)
00096     mContent->QueryInterface(NS_GET_IID(nsIDOMElement), (void**)aResult);
00097   else
00098     *aResult = nsnull;
00099   return NS_OK;
00100 }
00101 
00102 NS_IMETHODIMP
00103 nsBoxObject::GetLayoutManager(nsIBoxLayoutManager** aResult)
00104 {
00105   *aResult = mLayoutManager;
00106   NS_IF_ADDREF(*aResult);
00107   return NS_OK;
00108 }
00109 
00110 NS_IMETHODIMP
00111 nsBoxObject::SetLayoutManager(nsIBoxLayoutManager* aLayoutManager)
00112 {
00113   mLayoutManager = aLayoutManager;
00114   return NS_OK;
00115 }
00116 
00117 NS_IMETHODIMP
00118 nsBoxObject::GetPaintManager(nsIBoxPaintManager** aResult)
00119 {
00120   *aResult = mPaintManager;
00121   NS_IF_ADDREF(*aResult);
00122   return NS_OK;
00123 }
00124 
00125 NS_IMETHODIMP
00126 nsBoxObject::SetPaintManager(nsIBoxPaintManager* aPaintManager)
00127 {
00128   mPaintManager = aPaintManager;
00129   return NS_OK;
00130 }
00131 
00132 // nsPIBoxObject //////////////////////////////////////////////////////////////////////////
00133 
00134 NS_IMETHODIMP
00135 nsBoxObject::Init(nsIContent* aContent, nsIPresShell* aShell)
00136 {
00137   mContent = aContent;
00138   mPresShell = do_GetWeakReference(aShell);
00139   return NS_OK;
00140 }
00141 
00142 NS_IMETHODIMP
00143 nsBoxObject::SetDocument(nsIDocument* aDocument)
00144 {
00145   mPresState = nsnull;
00146   if (aDocument) {
00147     mPresShell = do_GetWeakReference(aDocument->GetShellAt(0));
00148   }
00149   else {
00150     mPresShell = nsnull;
00151     mContent = nsnull;
00152   }
00153   return NS_OK;
00154 }
00155 
00156 NS_IMETHODIMP
00157 nsBoxObject::InvalidatePresentationStuff()
00158 {
00159   mPresShell = nsnull;
00160 
00161   return NS_OK;
00162 }
00163 
00164 nsIFrame*
00165 nsBoxObject::GetFrame()
00166 {
00167   nsIFrame* frame = nsnull;
00168   nsCOMPtr<nsIPresShell> shell = GetPresShell();
00169   if (shell) {
00170     shell->FlushPendingNotifications(Flush_Frames);
00171     shell->GetPrimaryFrameFor(mContent, &frame);
00172   }
00173   return frame;
00174 }
00175 
00176 already_AddRefed<nsIPresShell>
00177 nsBoxObject::GetPresShell()
00178 {
00179   if (!mPresShell) {
00180     return nsnull;
00181   }
00182 
00183   nsIPresShell* shell = nsnull;
00184   CallQueryReferent(mPresShell.get(), &shell);
00185   return shell;
00186 }
00187 
00188 nsresult 
00189 nsBoxObject::GetOffsetRect(nsRect& aRect)
00190 {
00191   aRect.x = aRect.y = 0;
00192   aRect.Empty();
00193  
00194   if (!mContent)
00195     return NS_ERROR_NOT_INITIALIZED;
00196 
00197   nsresult res = NS_OK;
00198   nsCOMPtr<nsIDocument> doc = mContent->GetDocument();
00199 
00200   if (doc) {
00201     // Flush all pending notifications so that our frames are uptodate.  Must
00202     // do this before we get the presshell, since this can destroy presshells.
00203     doc->FlushPendingNotifications(Flush_Layout);
00204 
00205     // Get Presentation shell 0
00206     nsIPresShell *presShell = doc->GetShellAt(0);
00207 
00208     if(presShell) {
00209       // Get the Frame for our content
00210       nsIFrame* frame = nsnull;
00211       presShell->GetPrimaryFrameFor(mContent, &frame);
00212       if(frame != nsnull) {
00213         // Get its origin
00214         nsPoint origin = frame->GetPosition();
00215 
00216         // Get the union of all rectangles in this and continuation frames
00217         nsRect rcFrame;
00218         nsIFrame* next = frame;
00219         do {
00220           rcFrame.UnionRect(rcFrame, next->GetRect());
00221           next = next->GetNextInFlow();
00222         } while (nsnull != next);
00223         
00224 
00225         // Find the frame parent whose content's tagName either matches 
00226         // the tagName passed in or is the document element.
00227         nsIContent *docElement = doc->GetRootContent();
00228         nsIFrame* parent = frame->GetParent();
00229         while (parent) {
00230           // If we've hit the document element, break here
00231           if (parent->GetContent() == docElement) {
00232             break;
00233           }
00234 
00235           // Add the parent's origin to our own to get to the
00236           // right coordinate system
00237           origin += parent->GetPosition();
00238 
00239           parent = parent->GetParent();
00240         }
00241   
00242         // For the origin, add in the border for the frame
00243         const nsStyleBorder* border = frame->GetStyleBorder();
00244         origin.x += border->GetBorderWidth(NS_SIDE_LEFT);
00245         origin.y += border->GetBorderWidth(NS_SIDE_TOP);
00246 
00247         // And subtract out the border for the parent
00248         if (parent) {
00249           const nsStyleBorder* parentBorder = parent->GetStyleBorder();
00250           origin.x -= parentBorder->GetBorderWidth(NS_SIDE_LEFT);
00251           origin.y -= parentBorder->GetBorderWidth(NS_SIDE_TOP);
00252         }
00253 
00254         // Get the Presentation Context from the Shell
00255         nsPresContext *context = presShell->GetPresContext();
00256         if (context) {
00257           // Get the scale from that Presentation Context
00258           float scale;
00259           scale = context->TwipsToPixels();
00260               
00261           // Convert to pixels using that scale
00262           aRect.x = NSTwipsToIntPixels(origin.x, scale);
00263           aRect.y = NSTwipsToIntPixels(origin.y, scale);
00264           aRect.width = NSTwipsToIntPixels(rcFrame.width, scale);
00265           aRect.height = NSTwipsToIntPixels(rcFrame.height, scale);
00266         }
00267       }
00268     }
00269   }
00270  
00271   return res;
00272 }
00273 
00274 nsresult
00275 nsBoxObject::GetScreenPosition(nsIntPoint& aPoint)
00276 {
00277   aPoint.x = aPoint.y = 0;
00278   
00279   if (!mContent)
00280     return NS_ERROR_NOT_INITIALIZED;
00281 
00282   nsCOMPtr<nsIDocument> doc = mContent->GetDocument();
00283 
00284   if (doc) {
00285     // Get Presentation shell 0
00286     nsIPresShell *presShell = doc->GetShellAt(0);
00287 
00288     if (presShell) {
00289       // Flush all pending notifications so that our frames are uptodate
00290       doc->FlushPendingNotifications(Flush_Layout);
00291 
00292       nsPresContext *presContext = presShell->GetPresContext();
00293       if (presContext) {
00294         nsIFrame* frame;
00295         presShell->GetPrimaryFrameFor(mContent, &frame);
00296         
00297         if (frame) {
00298           nsIntRect rect = frame->GetScreenRect();
00299           aPoint.x = rect.x;
00300           aPoint.y = rect.y;
00301         }
00302       }
00303     }
00304   }
00305   
00306   return NS_OK;
00307 }
00308 
00309 NS_IMETHODIMP
00310 nsBoxObject::GetX(PRInt32* aResult)
00311 {
00312   nsRect rect;
00313   GetOffsetRect(rect);
00314   *aResult = rect.x;
00315   return NS_OK;
00316 }
00317 
00318 NS_IMETHODIMP 
00319 nsBoxObject::GetY(PRInt32* aResult)
00320 {
00321   nsRect rect;
00322   GetOffsetRect(rect);
00323   *aResult = rect.y;
00324   return NS_OK;
00325 }
00326 
00327 NS_IMETHODIMP
00328 nsBoxObject::GetWidth(PRInt32* aResult)
00329 {
00330   nsRect rect;
00331   GetOffsetRect(rect);
00332   *aResult = rect.width;
00333   return NS_OK;
00334 }
00335 
00336 NS_IMETHODIMP 
00337 nsBoxObject::GetHeight(PRInt32* aResult)
00338 {
00339   nsRect rect;
00340   GetOffsetRect(rect);
00341   *aResult = rect.height;
00342   return NS_OK;
00343 }
00344 
00345 NS_IMETHODIMP
00346 nsBoxObject::GetScreenX(PRInt32 *_retval)
00347 {
00348   nsIntPoint position;
00349   nsresult rv = GetScreenPosition(position);
00350   if (NS_FAILED(rv)) return rv;
00351   
00352   *_retval = position.x;
00353   
00354   return NS_OK;
00355 }
00356 
00357 NS_IMETHODIMP
00358 nsBoxObject::GetScreenY(PRInt32 *_retval)
00359 {
00360   nsIntPoint position;
00361   nsresult rv = GetScreenPosition(position);
00362   if (NS_FAILED(rv)) return rv;
00363   
00364   *_retval = position.y;
00365   
00366   return NS_OK;
00367 }
00368 
00369 NS_IMETHODIMP
00370 nsBoxObject::GetLookAndFeelMetric(const PRUnichar* aPropertyName, 
00371                                   PRUnichar** aResult)
00372 {
00373   nsCOMPtr<nsILookAndFeel> lookAndFeel(do_GetService(kLookAndFeelCID));
00374   if (!lookAndFeel)
00375     return NS_ERROR_FAILURE;
00376     
00377   nsAutoString property(aPropertyName);
00378   if (property.LowerCaseEqualsLiteral("scrollbarstyle")) {
00379     PRInt32 metricResult;
00380     lookAndFeel->GetMetric(nsILookAndFeel::eMetric_ScrollArrowStyle, metricResult);
00381     switch (metricResult) {
00382       case nsILookAndFeel::eMetric_ScrollArrowStyleBothAtBottom:
00383         *aResult = ToNewUnicode(NS_LITERAL_STRING("doublebottom"));
00384         break;
00385       case nsILookAndFeel::eMetric_ScrollArrowStyleBothAtEachEnd:
00386         *aResult = ToNewUnicode(NS_LITERAL_STRING("double"));
00387         break;
00388       case nsILookAndFeel::eMetric_ScrollArrowStyleBothAtTop:
00389         *aResult = ToNewUnicode(NS_LITERAL_STRING("doubletop"));
00390         break;
00391       default:
00392         *aResult = ToNewUnicode(NS_LITERAL_STRING("single"));
00393         break;   
00394     } 
00395   }
00396   else if (property.LowerCaseEqualsLiteral("thumbstyle")) {
00397     PRInt32 metricResult;
00398     lookAndFeel->GetMetric(nsILookAndFeel::eMetric_ScrollSliderStyle, metricResult);
00399     if ( metricResult == nsILookAndFeel::eMetric_ScrollThumbStyleNormal )
00400       *aResult = ToNewUnicode(NS_LITERAL_STRING("fixed"));
00401     else
00402       *aResult = ToNewUnicode(NS_LITERAL_STRING("proportional"));   
00403   }
00404   return NS_OK;
00405 }
00406 
00407 NS_IMETHODIMP
00408 nsBoxObject::GetPropertyAsSupports(const PRUnichar* aPropertyName, nsISupports** aResult)
00409 {
00410   if (!mPresState) {
00411     *aResult = nsnull;
00412     return NS_OK;
00413   }
00414 
00415   nsDependentString propertyName(aPropertyName);
00416   return mPresState->GetStatePropertyAsSupports(propertyName, aResult); // Addref here.
00417 }
00418 
00419 NS_IMETHODIMP
00420 nsBoxObject::SetPropertyAsSupports(const PRUnichar* aPropertyName, nsISupports* aValue)
00421 {
00422 #ifdef DEBUG
00423   if (aValue) {
00424     nsIFrame* frame;
00425     CallQueryInterface(aValue, &frame);
00426     NS_ASSERTION(!frame,
00427                  "Calling SetPropertyAsSupports on a frame.  Prepare to crash "
00428                  "and be exploited any time some random website decides to "
00429                  "exploit you");
00430   }
00431 #endif
00432 
00433   NS_ENSURE_ARG(aPropertyName && *aPropertyName);
00434   
00435   if (!mPresState) {
00436     NS_NewPresState(getter_Transfers(mPresState));
00437     NS_ENSURE_TRUE(mPresState, NS_ERROR_OUT_OF_MEMORY);
00438   }
00439 
00440   nsDependentString propertyName(aPropertyName);
00441   return mPresState->SetStatePropertyAsSupports(propertyName, aValue);
00442 }
00443 
00444 NS_IMETHODIMP
00445 nsBoxObject::GetProperty(const PRUnichar* aPropertyName, PRUnichar** aResult)
00446 {
00447   NS_ENSURE_ARG(aPropertyName && *aPropertyName);
00448   
00449   if (!mPresState) {
00450     *aResult = nsnull;
00451     return NS_OK;
00452   }
00453 
00454   nsDependentString propertyName(aPropertyName);
00455   nsAutoString result;
00456   nsresult rv = mPresState->GetStateProperty(propertyName, result);
00457   if (NS_FAILED(rv))
00458     return rv;
00459   *aResult = ToNewUnicode(result);
00460   return NS_OK;
00461 }
00462 
00463 NS_IMETHODIMP
00464 nsBoxObject::SetProperty(const PRUnichar* aPropertyName, const PRUnichar* aPropertyValue)
00465 {
00466   if (!mPresState)
00467     NS_NewPresState(getter_Transfers(mPresState));
00468 
00469   nsDependentString propertyName(aPropertyName);
00470   nsDependentString propertyValue(aPropertyValue);
00471   return mPresState->SetStateProperty(propertyName, propertyValue);
00472 }
00473 
00474 NS_IMETHODIMP
00475 nsBoxObject::RemoveProperty(const PRUnichar* aPropertyName)
00476 {
00477   if (!mPresState)
00478     return NS_OK;
00479 
00480   nsDependentString propertyName(aPropertyName);
00481   return mPresState->RemoveStateProperty(propertyName);
00482 }
00483 
00484 NS_IMETHODIMP 
00485 nsBoxObject::GetParentBox(nsIDOMElement * *aParentBox)
00486 {
00487   nsIFrame* frame = GetFrame();
00488   if (!frame) return NS_OK;
00489   nsIFrame* parent = frame->GetParent();
00490   if (!parent) return NS_OK;
00491 
00492   nsCOMPtr<nsIDOMElement> el = do_QueryInterface(parent->GetContent());
00493   *aParentBox = el;
00494   NS_IF_ADDREF(*aParentBox);
00495   return NS_OK;
00496 }
00497 
00498 NS_IMETHODIMP 
00499 nsBoxObject::GetFirstChild(nsIDOMElement * *aFirstVisibleChild)
00500 {
00501   *aFirstVisibleChild = nsnull;
00502   nsIFrame* frame = GetFrame();
00503   if (!frame) return NS_OK;
00504   nsIFrame* firstFrame = frame->GetFirstChild(nsnull);
00505   if (!firstFrame) return NS_OK;
00506   // get the content for the box and query to a dom element
00507   nsCOMPtr<nsIDOMElement> el = do_QueryInterface(firstFrame->GetContent());
00508   el.swap(*aFirstVisibleChild);
00509   return NS_OK;
00510 }
00511 
00512 NS_IMETHODIMP
00513 nsBoxObject::GetLastChild(nsIDOMElement * *aLastVisibleChild)
00514 {
00515   *aLastVisibleChild = nsnull;
00516   nsIFrame* frame = GetFrame();
00517   if (!frame) return NS_OK;
00518   return GetPreviousSibling(frame, nsnull, aLastVisibleChild);
00519 }
00520 
00521 NS_IMETHODIMP
00522 nsBoxObject::GetNextSibling(nsIDOMElement **aNextOrdinalSibling)
00523 {
00524   *aNextOrdinalSibling = nsnull;
00525   nsIFrame* frame = GetFrame();
00526   if (!frame) return NS_OK;
00527   nsIFrame* nextFrame = frame->GetNextSibling();
00528   if (!nextFrame) return NS_OK;
00529   // get the content for the box and query to a dom element
00530   nsCOMPtr<nsIDOMElement> el = do_QueryInterface(nextFrame->GetContent());
00531   el.swap(*aNextOrdinalSibling);
00532   return NS_OK;
00533 }
00534 
00535 NS_IMETHODIMP
00536 nsBoxObject::GetPreviousSibling(nsIDOMElement **aPreviousOrdinalSibling)
00537 {
00538   *aPreviousOrdinalSibling = nsnull;
00539   nsIFrame* frame = GetFrame();
00540   if (!frame) return NS_OK;
00541   nsIFrame* parentFrame = frame->GetParent();
00542   if (!parentFrame) return NS_OK;
00543   return GetPreviousSibling(parentFrame, frame, aPreviousOrdinalSibling);
00544 }
00545 
00546 nsresult
00547 nsBoxObject::GetPreviousSibling(nsIFrame* aParentFrame, nsIFrame* aFrame,
00548                                 nsIDOMElement** aResult)
00549 {
00550   nsIFrame* nextFrame = aParentFrame->GetFirstChild(nsnull);
00551   nsIFrame* prevFrame = nsnull;
00552   while (nextFrame) {
00553     if (nextFrame == aFrame)
00554       break;
00555     prevFrame = nextFrame;
00556     nextFrame = nextFrame->GetNextSibling();
00557   }
00558    
00559   if (!prevFrame) return NS_OK;
00560   // get the content for the box and query to a dom element
00561   nsCOMPtr<nsIDOMElement> el = do_QueryInterface(prevFrame->GetContent());
00562   el.swap(*aResult);
00563   return NS_OK;
00564 }
00565 
00566 nsresult
00567 nsBoxObject::GetDocShell(nsIDocShell** aResult)
00568 {
00569   *aResult = nsnull;
00570 
00571   nsIFrame *frame = GetFrame();
00572 
00573   if (frame) {
00574     nsIFrameFrame *frame_frame = nsnull;
00575     CallQueryInterface(frame, &frame_frame);
00576 
00577     if (frame_frame) {
00578       // Ok, the frame for mContent is a nsIFrameFrame, it knows how
00579       // to reach the docshell, so ask it...
00580 
00581       return frame_frame->GetDocShell(aResult);
00582     }
00583   }
00584 
00585   // No nsIFrameFrame available for mContent, try if there's a mapping
00586   // between mContent's document to mContent's subdocument.
00587 
00588   if (!mContent) {
00589     return NS_OK;
00590   }
00591 
00592   // XXXbz sXBL/XBL2 issue -- ownerDocument or currentDocument?
00593   nsIDocument *doc = mContent->GetDocument();
00594 
00595   if (!doc) {
00596     return NS_OK;
00597   }
00598   
00599   nsIDocument *sub_doc = doc->GetSubDocumentFor(mContent);
00600 
00601   if (!sub_doc) {
00602     return NS_OK;
00603   }
00604 
00605   nsCOMPtr<nsISupports> container = sub_doc->GetContainer();
00606 
00607   if (!container) {
00608     return NS_OK;
00609   }
00610 
00611   return CallQueryInterface(container, aResult);
00612 }
00613 
00614 
00615 // Creation Routine ///////////////////////////////////////////////////////////////////////
00616 
00617 nsresult
00618 NS_NewBoxObject(nsIBoxObject** aResult)
00619 {
00620   *aResult = new nsBoxObject;
00621   if (!*aResult)
00622     return NS_ERROR_OUT_OF_MEMORY;
00623   NS_ADDREF(*aResult);
00624   return NS_OK;
00625 }
00626