Back to index

lightning-sunbird  0.9+nobinonly
nsImageDocument.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  *   Morten Nilsen <morten@nilsen.com>
00024  *   Christian Biesinger <cbiesinger@web.de>
00025  *   Jan Varga <varga@ku.sk>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #include "nsRect.h"
00042 #include "nsHTMLDocument.h"
00043 #include "nsIImageDocument.h"
00044 #include "nsIImageLoadingContent.h"
00045 #include "nsGenericHTMLElement.h"
00046 #include "nsIDOMHTMLImageElement.h"
00047 #include "nsIDOMEvent.h"
00048 #include "nsIDOMKeyEvent.h"
00049 #include "nsIDOMMouseEvent.h"
00050 #include "nsIDOMEventListener.h"
00051 #include "nsHTMLAtoms.h"
00052 #include "imgIRequest.h"
00053 #include "imgILoader.h"
00054 #include "imgIContainer.h"
00055 #include "imgIDecoderObserver.h"
00056 #include "nsIPresShell.h"
00057 #include "nsPresContext.h"
00058 #include "nsIScrollableView.h"
00059 #include "nsStyleContext.h"
00060 #include "nsAutoPtr.h"
00061 #include "nsMediaDocument.h"
00062 #include "nsStyleSet.h"
00063 #include "nsIChannel.h"
00064 #include "nsIContentPolicy.h"
00065 #include "nsContentPolicyUtils.h"
00066 #include "nsPIDOMWindow.h"
00067 #include "nsIDOMElement.h"
00068 #include "nsIDOMNSHTMLElement.h"
00069 
00070 #define AUTOMATIC_IMAGE_RESIZING_PREF "browser.enable_automatic_image_resizing"
00071 
00072 class nsImageDocument;
00073 
00074 class ImageListener: public nsMediaDocumentStreamListener
00075 {
00076 public:
00077   ImageListener(nsImageDocument* aDocument);
00078   virtual ~ImageListener();
00079 
00080   NS_DECL_ISUPPORTS
00081 
00082   NS_DECL_NSIREQUESTOBSERVER
00083 };
00084 
00085 class nsImageDocument : public nsMediaDocument,
00086                         public nsIImageDocument,
00087                         public imgIDecoderObserver,
00088                         public nsIDOMEventListener
00089 {
00090 public:
00091   nsImageDocument();
00092   virtual ~nsImageDocument();
00093 
00094   NS_DECL_ISUPPORTS
00095 
00096   virtual nsresult Init();
00097 
00098   virtual nsresult StartDocumentLoad(const char*         aCommand,
00099                                      nsIChannel*         aChannel,
00100                                      nsILoadGroup*       aLoadGroup,
00101                                      nsISupports*        aContainer,
00102                                      nsIStreamListener** aDocListener,
00103                                      PRBool              aReset = PR_TRUE,
00104                                      nsIContentSink*     aSink = nsnull);
00105 
00106   virtual void SetScriptGlobalObject(nsIScriptGlobalObject* aScriptGlobalObject);
00107   virtual void Destroy();
00108 
00109   NS_DECL_NSIIMAGEDOCUMENT
00110 
00111   NS_DECL_IMGIDECODEROBSERVER
00112 
00113   NS_DECL_IMGICONTAINEROBSERVER
00114 
00115   // nsIDOMEventListener
00116   NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent);
00117 
00118   friend class ImageListener;
00119 protected:
00120   nsresult CreateSyntheticDocument();
00121 
00122   nsresult CheckOverflowing(PRBool changeState);
00123 
00124   void UpdateTitleAndCharset();
00125 
00126   float GetRatio() {
00127     return PR_MIN((float)mVisibleWidth / mImageWidth,
00128                   (float)mVisibleHeight / mImageHeight);
00129   }
00130 
00131   nsCOMPtr<nsIContent>          mImageContent;
00132 
00133   PRInt32                       mVisibleWidth;
00134   PRInt32                       mVisibleHeight;
00135   PRInt32                       mImageWidth;
00136   PRInt32                       mImageHeight;
00137 
00138   PRPackedBool                  mResizeImageByDefault;
00139   PRPackedBool                  mImageIsOverflowing;
00140   // mImageIsResized is true if the image is currently resized
00141   PRPackedBool                  mImageIsResized;
00142   // mShouldResize is true if the image should be resized when it doesn't fit
00143   // mImageIsResized cannot be true when this is false, but mImageIsResized
00144   // can be false when this is true
00145   PRPackedBool                  mShouldResize;
00146   PRPackedBool                  mFirstResize;
00147 };
00148 
00149 NS_IMPL_ADDREF_INHERITED(ImageListener, nsMediaDocumentStreamListener)
00150 NS_IMPL_RELEASE_INHERITED(ImageListener, nsMediaDocumentStreamListener)
00151 
00152 NS_INTERFACE_MAP_BEGIN(ImageListener)
00153 NS_INTERFACE_MAP_END_INHERITING(nsMediaDocumentStreamListener)
00154 
00155 ImageListener::ImageListener(nsImageDocument* aDocument)
00156   : nsMediaDocumentStreamListener(aDocument)
00157 {
00158 }
00159 
00160 
00161 ImageListener::~ImageListener()
00162 {
00163 }
00164 
00165 NS_IMETHODIMP
00166 ImageListener::OnStartRequest(nsIRequest* request, nsISupports *ctxt)
00167 {
00168   NS_ENSURE_TRUE(mDocument, NS_ERROR_FAILURE);
00169 
00170   nsImageDocument *imgDoc = (nsImageDocument*)mDocument.get();
00171   nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
00172   if (!channel) {
00173     return NS_ERROR_FAILURE;
00174   }
00175 
00176   nsCOMPtr<nsPIDOMWindow> domWindow =
00177     do_QueryInterface(imgDoc->GetScriptGlobalObject());
00178   NS_ENSURE_TRUE(domWindow, NS_ERROR_UNEXPECTED);
00179 
00180   // Do a ShouldProcess check to see whether to keep loading the image.
00181   nsCOMPtr<nsIURI> channelURI;
00182   channel->GetURI(getter_AddRefs(channelURI));
00183 
00184   nsCAutoString mimeType;
00185   channel->GetContentType(mimeType);
00186     
00187   PRInt16 decision = nsIContentPolicy::ACCEPT;
00188   nsresult rv = NS_CheckContentProcessPolicy(nsIContentPolicy::TYPE_IMAGE,
00189                                              channelURI,
00190                                              nsnull,
00191                                              domWindow->GetFrameElementInternal(),
00192                                              mimeType,
00193                                              nsnull,
00194                                              &decision);
00195                                                
00196   if (NS_FAILED(rv) || NS_CP_REJECTED(decision)) {
00197     request->Cancel(NS_ERROR_CONTENT_BLOCKED);
00198     return NS_OK;
00199   }
00200 
00201   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(imgDoc->mImageContent);
00202   NS_ENSURE_TRUE(imageLoader, NS_ERROR_UNEXPECTED);
00203 
00204   imageLoader->AddObserver(imgDoc);
00205   imageLoader->LoadImageWithChannel(channel, getter_AddRefs(mNextStream));
00206 
00207   return nsMediaDocumentStreamListener::OnStartRequest(request, ctxt);
00208 }
00209 
00210 NS_IMETHODIMP
00211 ImageListener::OnStopRequest(nsIRequest* request, nsISupports *ctxt,
00212                              nsresult status)
00213 {
00214   NS_ENSURE_TRUE(mDocument, NS_ERROR_FAILURE);
00215   nsImageDocument *imgDoc = (nsImageDocument*)mDocument.get();
00216   imgDoc->UpdateTitleAndCharset();
00217   
00218   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(imgDoc->mImageContent);
00219   if (imageLoader) {
00220     imageLoader->RemoveObserver(imgDoc);
00221   }
00222 
00223 
00224   // mImageContent can be null if the document is already destroyed
00225   if (NS_FAILED(status) && imgDoc->mStringBundle && imgDoc->mImageContent) {
00226     nsCAutoString src;
00227     imgDoc->mDocumentURI->GetSpec(src);
00228     NS_ConvertUTF8toUTF16 srcString(src);
00229     const PRUnichar* formatString[] = { srcString.get() };
00230     nsXPIDLString errorMsg;
00231     NS_NAMED_LITERAL_STRING(str, "InvalidImage");
00232     imgDoc->mStringBundle->FormatStringFromName(str.get(), formatString, 1,
00233                                                 getter_Copies(errorMsg));
00234     
00235     imgDoc->mImageContent->SetAttr(kNameSpaceID_None, nsHTMLAtoms::alt, errorMsg, PR_FALSE);
00236   }
00237 
00238   return nsMediaDocumentStreamListener::OnStopRequest(request, ctxt, status);
00239 }
00240 
00241 
00242   // NOTE! nsDocument::operator new() zeroes out all members, so don't
00243   // bother initializing members to 0.
00244 
00245 nsImageDocument::nsImageDocument()
00246 {
00247 
00248   // NOTE! nsDocument::operator new() zeroes out all members, so don't
00249   // bother initializing members to 0.
00250 
00251 }
00252 
00253 nsImageDocument::~nsImageDocument()
00254 {
00255 }
00256 
00257 NS_IMPL_ADDREF_INHERITED(nsImageDocument, nsMediaDocument)
00258 NS_IMPL_RELEASE_INHERITED(nsImageDocument, nsMediaDocument)
00259 
00260 NS_INTERFACE_MAP_BEGIN(nsImageDocument)
00261   NS_INTERFACE_MAP_ENTRY(nsIImageDocument)
00262   NS_INTERFACE_MAP_ENTRY(imgIDecoderObserver)
00263   NS_INTERFACE_MAP_ENTRY(imgIContainerObserver)
00264   NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
00265   NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(ImageDocument)
00266 NS_INTERFACE_MAP_END_INHERITING(nsMediaDocument)
00267 
00268 
00269 nsresult
00270 nsImageDocument::Init()
00271 {
00272   nsresult rv = nsMediaDocument::Init();
00273   NS_ENSURE_SUCCESS(rv, rv);
00274 
00275   mResizeImageByDefault =
00276     nsContentUtils::GetBoolPref(AUTOMATIC_IMAGE_RESIZING_PREF);
00277   mShouldResize = mResizeImageByDefault;
00278   mFirstResize = PR_TRUE;
00279 
00280   return NS_OK;
00281 }
00282 
00283 nsresult
00284 nsImageDocument::StartDocumentLoad(const char*         aCommand,
00285                                    nsIChannel*         aChannel,
00286                                    nsILoadGroup*       aLoadGroup,
00287                                    nsISupports*        aContainer,
00288                                    nsIStreamListener** aDocListener,
00289                                    PRBool              aReset,
00290                                    nsIContentSink*     aSink)
00291 {
00292   nsresult rv =
00293     nsMediaDocument::StartDocumentLoad(aCommand, aChannel, aLoadGroup,
00294                                        aContainer, aDocListener, aReset,
00295                                        aSink);
00296   if (NS_FAILED(rv)) {
00297     return rv;
00298   }
00299 
00300   NS_ASSERTION(aDocListener, "null aDocListener");
00301   *aDocListener = new ImageListener(this);
00302   if (!*aDocListener)
00303     return NS_ERROR_OUT_OF_MEMORY;
00304   NS_ADDREF(*aDocListener);
00305 
00306   return NS_OK;
00307 }
00308 
00309 void
00310 nsImageDocument::Destroy()
00311 {
00312   if (mImageContent) {
00313     // Remove our event listener from the image content.
00314     nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(mImageContent);
00315     target->RemoveEventListener(NS_LITERAL_STRING("click"), this, PR_FALSE);
00316 
00317     // Break reference cycle with mImageContent, if we have one
00318     nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mImageContent);
00319     if (imageLoader) {
00320       imageLoader->RemoveObserver(this);
00321     }
00322 
00323     mImageContent = nsnull;
00324   }
00325 
00326   nsMediaDocument::Destroy();
00327 }
00328 
00329 void
00330 nsImageDocument::SetScriptGlobalObject(nsIScriptGlobalObject* aScriptGlobalObject)
00331 {
00332   // If the script global object is changing, we need to unhook our event
00333   // listeners on the window.
00334   nsCOMPtr<nsIDOMEventTarget> target;
00335   if (mScriptGlobalObject &&
00336       aScriptGlobalObject != mScriptGlobalObject) {
00337     target = do_QueryInterface(mScriptGlobalObject);
00338     target->RemoveEventListener(NS_LITERAL_STRING("resize"), this, PR_FALSE);
00339     target->RemoveEventListener(NS_LITERAL_STRING("keypress"), this,
00340                                 PR_FALSE);
00341   }
00342 
00343   // Set the script global object on the superclass before doing
00344   // anything that might require it....
00345   nsHTMLDocument::SetScriptGlobalObject(aScriptGlobalObject);
00346 
00347   if (aScriptGlobalObject) {
00348     if (!mRootContent) {
00349       // Create synthetic document
00350       nsresult rv = CreateSyntheticDocument();
00351       NS_ASSERTION(NS_SUCCEEDED(rv), "failed to create synthetic document");
00352 
00353       target = do_QueryInterface(mImageContent);
00354       target->AddEventListener(NS_LITERAL_STRING("click"), this, PR_FALSE);
00355     }
00356 
00357     target = do_QueryInterface(aScriptGlobalObject);
00358     target->AddEventListener(NS_LITERAL_STRING("resize"), this, PR_FALSE);
00359     target->AddEventListener(NS_LITERAL_STRING("keypress"), this, PR_FALSE);
00360   }
00361 }
00362 
00363 
00364 NS_IMETHODIMP
00365 nsImageDocument::GetImageResizingEnabled(PRBool* aImageResizingEnabled)
00366 {
00367   *aImageResizingEnabled = PR_TRUE;
00368   return NS_OK;
00369 }
00370 
00371 NS_IMETHODIMP
00372 nsImageDocument::GetImageIsOverflowing(PRBool* aImageIsOverflowing)
00373 {
00374   *aImageIsOverflowing = mImageIsOverflowing;
00375   return NS_OK;
00376 }
00377 
00378 NS_IMETHODIMP
00379 nsImageDocument::GetImageIsResized(PRBool* aImageIsResized)
00380 {
00381   *aImageIsResized = mImageIsResized;
00382   return NS_OK;
00383 }
00384 
00385 NS_IMETHODIMP
00386 nsImageDocument::GetImageRequest(imgIRequest** aImageRequest)
00387 {
00388   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mImageContent);
00389   if (imageLoader) {
00390     return imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
00391                                    aImageRequest);
00392   }
00393 
00394   *aImageRequest = nsnull;
00395   return NS_OK;
00396 }
00397 
00398 NS_IMETHODIMP
00399 nsImageDocument::ShrinkToFit()
00400 {
00401   nsCOMPtr<nsIDOMHTMLImageElement> image = do_QueryInterface(mImageContent);
00402   image->SetWidth(NSToCoordFloor(GetRatio() * mImageWidth));
00403   
00404   mImageContent->SetAttr(kNameSpaceID_None, nsHTMLAtoms::style,
00405                          NS_LITERAL_STRING("cursor: -moz-zoom-in"), PR_TRUE);
00406   
00407   mImageIsResized = PR_TRUE;
00408   
00409   UpdateTitleAndCharset();
00410 
00411   return NS_OK;
00412 }
00413 
00414 NS_IMETHODIMP
00415 nsImageDocument::RestoreImageTo(PRInt32 aX, PRInt32 aY)
00416 {
00417   float ratio = GetRatio();
00418 
00419   RestoreImage();
00420   FlushPendingNotifications(Flush_Layout);
00421 
00422   nsIPresShell *shell = GetShellAt(0);
00423   if (!shell)
00424     return NS_OK;
00425 
00426   nsPresContext* context = shell->GetPresContext();
00427   if (!context)
00428     return NS_OK;
00429 
00430   nsIViewManager* vm = context->GetViewManager();
00431   if (!vm)
00432     return NS_OK;
00433 
00434   nsIScrollableView* view;
00435   vm->GetRootScrollableView(&view);
00436   if (!view)
00437     return NS_OK;
00438 
00439   nsSize scrolledSize;
00440   if (NS_FAILED(view->GetContainerSize(&scrolledSize.width, &scrolledSize.height)))
00441     return NS_OK;
00442 
00443   nsRect portRect = view->View()->GetBounds();
00444   view->ScrollTo(NSToCoordRound((aX/ratio)*context->PixelsToTwips() - portRect.width/2),
00445                  NSToCoordRound((aY/ratio)*context->PixelsToTwips() - portRect.height/2),
00446                  NS_VMREFRESH_IMMEDIATE);
00447   return NS_OK;
00448 }
00449 
00450 NS_IMETHODIMP
00451 nsImageDocument::RestoreImage()
00452 {
00453   mImageContent->UnsetAttr(kNameSpaceID_None, nsHTMLAtoms::width, PR_TRUE);
00454   
00455   if (mImageIsOverflowing) {
00456     mImageContent->SetAttr(kNameSpaceID_None, nsHTMLAtoms::style,
00457                            NS_LITERAL_STRING("cursor: -moz-zoom-out"), PR_TRUE);
00458   }
00459   else {
00460     mImageContent->UnsetAttr(kNameSpaceID_None, nsHTMLAtoms::style, PR_TRUE);
00461   }
00462   
00463   mImageIsResized = PR_FALSE;
00464   
00465   UpdateTitleAndCharset();
00466 
00467   return NS_OK;
00468 }
00469 
00470 NS_IMETHODIMP
00471 nsImageDocument::ToggleImageSize()
00472 {
00473   mShouldResize = PR_TRUE;
00474   if (mImageIsResized) {
00475     mShouldResize = PR_FALSE;
00476     RestoreImage();
00477   }
00478   else if (mImageIsOverflowing) {
00479     ShrinkToFit();
00480   }
00481 
00482   return NS_OK;
00483 }
00484 
00485 NS_IMETHODIMP
00486 nsImageDocument::OnStartDecode(imgIRequest* aRequest)
00487 {
00488   return NS_OK;
00489 }
00490 
00491 NS_IMETHODIMP
00492 nsImageDocument::OnStartContainer(imgIRequest* aRequest, imgIContainer* aImage)
00493 {
00494   aImage->GetWidth(&mImageWidth);
00495   aImage->GetHeight(&mImageHeight);
00496   CheckOverflowing(mResizeImageByDefault);
00497   UpdateTitleAndCharset();
00498 
00499   return NS_OK;
00500 }
00501 
00502 NS_IMETHODIMP
00503 nsImageDocument::OnStartFrame(imgIRequest* aRequest, gfxIImageFrame* aFrame)
00504 {
00505   return NS_OK;
00506 }
00507 
00508 NS_IMETHODIMP
00509 nsImageDocument::OnDataAvailable(imgIRequest* aRequest,
00510                                  gfxIImageFrame* aFrame,
00511                                  const nsRect* aRect)
00512 {
00513   return NS_OK;
00514 }
00515 
00516 NS_IMETHODIMP
00517 nsImageDocument::OnStopFrame(imgIRequest* aRequest,
00518                              gfxIImageFrame* aFrame)
00519 {
00520   return NS_OK;
00521 }
00522 
00523 NS_IMETHODIMP
00524 nsImageDocument::OnStopContainer(imgIRequest* aRequest,
00525                                  imgIContainer* aImage)
00526 {
00527   return NS_OK;
00528 }
00529 
00530 NS_IMETHODIMP
00531 nsImageDocument::OnStopDecode(imgIRequest* aRequest,
00532                               nsresult status,
00533                               const PRUnichar* statusArg)
00534 {
00535   return NS_OK;
00536 }
00537 
00538 NS_IMETHODIMP
00539 nsImageDocument::FrameChanged(imgIContainer* aContainer,
00540                               gfxIImageFrame* aFrame,
00541                               nsRect* aDirtyRect)
00542 {
00543   return NS_OK;
00544 }
00545 
00546 
00547 NS_IMETHODIMP
00548 nsImageDocument::HandleEvent(nsIDOMEvent* aEvent)
00549 {
00550   nsAutoString eventType;
00551   aEvent->GetType(eventType);
00552   if (eventType.EqualsLiteral("resize")) {
00553     CheckOverflowing(PR_FALSE);
00554   }
00555   else if (eventType.EqualsLiteral("click")) {
00556     mShouldResize = PR_TRUE;
00557     if (mImageIsResized) {
00558       PRInt32 x = 0, y = 0;
00559       nsCOMPtr<nsIDOMMouseEvent> event(do_QueryInterface(aEvent));
00560       if (event) {
00561         event->GetClientX(&x);
00562         event->GetClientY(&y);
00563         PRInt32 left = 0, top = 0;
00564         nsCOMPtr<nsIDOMNSHTMLElement> nsElement(do_QueryInterface(mImageContent));
00565         nsElement->GetOffsetLeft(&left);
00566         nsElement->GetOffsetTop(&top);
00567         x -= left;
00568         y -= top;
00569       }
00570       mShouldResize = PR_FALSE;
00571       RestoreImageTo(x, y);
00572     }
00573     else if (mImageIsOverflowing) {
00574       ShrinkToFit();
00575     }
00576   }
00577   else if (eventType.EqualsLiteral("keypress")) {
00578     nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aEvent);
00579     PRUint32 charCode;
00580     keyEvent->GetCharCode(&charCode);
00581     // plus key
00582     if (charCode == 0x2B) {
00583       mShouldResize = PR_FALSE;
00584       if (mImageIsResized) {
00585         RestoreImage();
00586       }
00587     }
00588     // minus key
00589     else if (charCode == 0x2D) {
00590       mShouldResize = PR_TRUE;
00591       if (mImageIsOverflowing) {
00592         ShrinkToFit();
00593       }
00594     }
00595   }
00596 
00597   return NS_OK;
00598 }
00599 
00600 nsresult
00601 nsImageDocument::CreateSyntheticDocument()
00602 {
00603   // Synthesize an html document that refers to the image
00604   nsresult rv = nsMediaDocument::CreateSyntheticDocument();
00605   NS_ENSURE_SUCCESS(rv, rv);
00606 
00607   nsCOMPtr<nsIContent> body = do_QueryInterface(mBodyContent);
00608   if (!body) {
00609     NS_WARNING("no body on image document!");
00610     return NS_ERROR_FAILURE;
00611   }
00612 
00613   nsCOMPtr<nsINodeInfo> nodeInfo;
00614   rv = mNodeInfoManager->GetNodeInfo(nsHTMLAtoms::img, nsnull,
00615                                      kNameSpaceID_None,
00616                                      getter_AddRefs(nodeInfo));
00617   NS_ENSURE_SUCCESS(rv, rv);
00618 
00619   mImageContent = NS_NewHTMLImageElement(nodeInfo);
00620   if (!mImageContent) {
00621     return NS_ERROR_OUT_OF_MEMORY;
00622   }
00623   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mImageContent);
00624   NS_ENSURE_TRUE(imageLoader, NS_ERROR_UNEXPECTED);
00625 
00626   nsCAutoString src;
00627   mDocumentURI->GetSpec(src);
00628 
00629   NS_ConvertUTF8toUCS2 srcString(src);
00630   // Make sure not to start the image load from here...
00631   imageLoader->SetLoadingEnabled(PR_FALSE);
00632   mImageContent->SetAttr(kNameSpaceID_None, nsHTMLAtoms::src, srcString, PR_FALSE);
00633   mImageContent->SetAttr(kNameSpaceID_None, nsHTMLAtoms::alt, srcString, PR_FALSE);
00634 
00635   body->AppendChildTo(mImageContent, PR_FALSE);
00636   imageLoader->SetLoadingEnabled(PR_TRUE);
00637 
00638   return NS_OK;
00639 }
00640 
00641 nsresult
00642 nsImageDocument::CheckOverflowing(PRBool changeState)
00643 {
00644   nsIPresShell *shell = GetShellAt(0);
00645   if (!shell) {
00646     return NS_OK;
00647   }
00648 
00649   nsPresContext *context = shell->GetPresContext();
00650   nsRect visibleArea = context->GetVisibleArea();
00651 
00652   nsCOMPtr<nsIContent> content = do_QueryInterface(mBodyContent);
00653   if (!content) {
00654     NS_WARNING("no body on image document!");
00655     return NS_ERROR_FAILURE;
00656   }
00657 
00658   nsRefPtr<nsStyleContext> styleContext =
00659     context->StyleSet()->ResolveStyleFor(content, nsnull);
00660 
00661   const nsStyleMargin* marginData = styleContext->GetStyleMargin();
00662   nsMargin margin;
00663   marginData->GetMargin(margin);
00664   visibleArea.Deflate(margin);
00665 
00666   nsStyleBorderPadding bPad;
00667   styleContext->GetBorderPaddingFor(bPad);
00668   bPad.GetBorderPadding(margin);
00669   visibleArea.Deflate(margin);
00670 
00671   float t2p;
00672   t2p = context->TwipsToPixels();
00673   mVisibleWidth = NSTwipsToIntPixels(visibleArea.width, t2p);
00674   mVisibleHeight = NSTwipsToIntPixels(visibleArea.height, t2p);
00675 
00676   mImageIsOverflowing =
00677     mImageWidth > mVisibleWidth || mImageHeight > mVisibleHeight;
00678 
00679   if (changeState || mShouldResize || mFirstResize) {
00680     if (mImageIsOverflowing && (changeState || mShouldResize)) {
00681       ShrinkToFit();
00682     }
00683     else if (mImageIsResized || mFirstResize) {
00684       RestoreImage();
00685     }
00686   }
00687   mFirstResize = PR_FALSE;
00688 
00689   return NS_OK;
00690 }
00691 
00692 void 
00693 nsImageDocument::UpdateTitleAndCharset()
00694 {
00695   nsCAutoString typeStr;
00696   nsCOMPtr<imgIRequest> imageRequest;
00697   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mImageContent);
00698   if (imageLoader) {
00699     imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
00700                             getter_AddRefs(imageRequest));
00701   }
00702     
00703   if (imageRequest) {
00704     nsXPIDLCString mimeType;
00705     imageRequest->GetMimeType(getter_Copies(mimeType));
00706     ToUpperCase(mimeType);
00707     nsXPIDLCString::const_iterator start, end;
00708     mimeType.BeginReading(start);
00709     mimeType.EndReading(end);
00710     nsXPIDLCString::const_iterator iter = end;
00711     if (FindInReadable(NS_LITERAL_CSTRING("IMAGE/"), start, iter) && 
00712         iter != end) {
00713       // strip out "X-" if any
00714       if (*iter == 'X') {
00715         ++iter;
00716         if (iter != end && *iter == '-') {
00717           ++iter;
00718           if (iter == end) {
00719             // looks like "IMAGE/X-" is the type??  Bail out of here.
00720             mimeType.BeginReading(iter);
00721           }
00722         } else {
00723           --iter;
00724         }
00725       }
00726       typeStr = Substring(iter, end);
00727     } else {
00728       typeStr = mimeType;
00729     }
00730   }
00731 
00732   nsXPIDLString status;
00733   if (mImageIsResized) {
00734     nsAutoString ratioStr;
00735     ratioStr.AppendInt(NSToCoordFloor(GetRatio() * 100));
00736 
00737     const PRUnichar* formatString[1] = { ratioStr.get() };
00738     mStringBundle->FormatStringFromName(NS_LITERAL_STRING("ScaledImage").get(),
00739                                         formatString, 1,
00740                                         getter_Copies(status));
00741   }
00742 
00743   static const char* const formatNames[4] = 
00744   {
00745     "ImageTitleWithNeitherDimensionsNorFile",
00746     "ImageTitleWithoutDimensions",
00747     "ImageTitleWithDimensions",
00748     "ImageTitleWithDimensionsAndFile",
00749   };
00750 
00751   nsMediaDocument::UpdateTitleAndCharset(typeStr, formatNames,
00752                                          mImageWidth, mImageHeight, status);
00753 }
00754 
00755 
00756 nsresult
00757 NS_NewImageDocument(nsIDocument** aResult)
00758 {
00759   nsImageDocument* doc = new nsImageDocument();
00760   if (!doc) {
00761     return NS_ERROR_OUT_OF_MEMORY;
00762   }
00763 
00764   NS_ADDREF(doc);
00765   nsresult rv = doc->Init();
00766 
00767   if (NS_FAILED(rv)) {
00768     NS_RELEASE(doc);
00769   }
00770 
00771   *aResult = doc;
00772 
00773   return rv;
00774 }