content/html/document/src/ImageDocument.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "ImageDocument.h"
     7 #include "mozilla/dom/ImageDocumentBinding.h"
     8 #include "nsRect.h"
     9 #include "nsIImageLoadingContent.h"
    10 #include "nsGenericHTMLElement.h"
    11 #include "nsDocShell.h"
    12 #include "nsIDocumentInlines.h"
    13 #include "nsDOMTokenList.h"
    14 #include "nsIDOMHTMLImageElement.h"
    15 #include "nsIDOMEvent.h"
    16 #include "nsIDOMKeyEvent.h"
    17 #include "nsIDOMMouseEvent.h"
    18 #include "nsIDOMEventListener.h"
    19 #include "nsIFrame.h"
    20 #include "nsGkAtoms.h"
    21 #include "imgIRequest.h"
    22 #include "imgILoader.h"
    23 #include "imgIContainer.h"
    24 #include "imgINotificationObserver.h"
    25 #include "nsIPresShell.h"
    26 #include "nsPresContext.h"
    27 #include "nsStyleContext.h"
    28 #include "nsAutoPtr.h"
    29 #include "nsStyleSet.h"
    30 #include "nsIChannel.h"
    31 #include "nsIContentPolicy.h"
    32 #include "nsContentPolicyUtils.h"
    33 #include "nsPIDOMWindow.h"
    34 #include "nsIDOMElement.h"
    35 #include "nsIDOMHTMLElement.h"
    36 #include "nsError.h"
    37 #include "nsURILoader.h"
    38 #include "nsIDocShell.h"
    39 #include "nsIContentViewer.h"
    40 #include "nsIMarkupDocumentViewer.h"
    41 #include "nsThreadUtils.h"
    42 #include "nsIScrollableFrame.h"
    43 #include "nsContentUtils.h"
    44 #include "mozilla/dom/Element.h"
    45 #include "mozilla/Preferences.h"
    46 #include <algorithm>
    48 #define AUTOMATIC_IMAGE_RESIZING_PREF "browser.enable_automatic_image_resizing"
    49 #define CLICK_IMAGE_RESIZING_PREF "browser.enable_click_image_resizing"
    50 //XXX A hack needed for Firefox's site specific zoom.
    51 #define SITE_SPECIFIC_ZOOM "browser.zoom.siteSpecific"
    53 namespace mozilla {
    54 namespace dom {
    56 class ImageListener : public MediaDocumentStreamListener
    57 {
    58 public:
    59   NS_DECL_NSIREQUESTOBSERVER
    61   ImageListener(ImageDocument* aDocument);
    62   virtual ~ImageListener();
    63 };
    65 ImageListener::ImageListener(ImageDocument* aDocument)
    66   : MediaDocumentStreamListener(aDocument)
    67 {
    68 }
    70 ImageListener::~ImageListener()
    71 {
    72 }
    74 NS_IMETHODIMP
    75 ImageListener::OnStartRequest(nsIRequest* request, nsISupports *ctxt)
    76 {
    77   NS_ENSURE_TRUE(mDocument, NS_ERROR_FAILURE);
    79   ImageDocument *imgDoc = static_cast<ImageDocument*>(mDocument.get());
    80   nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
    81   if (!channel) {
    82     return NS_ERROR_FAILURE;
    83   }
    85   nsCOMPtr<nsPIDOMWindow> domWindow = imgDoc->GetWindow();
    86   NS_ENSURE_TRUE(domWindow, NS_ERROR_UNEXPECTED);
    88   // Do a ShouldProcess check to see whether to keep loading the image.
    89   nsCOMPtr<nsIURI> channelURI;
    90   channel->GetURI(getter_AddRefs(channelURI));
    92   nsAutoCString mimeType;
    93   channel->GetContentType(mimeType);
    95   nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
    96   nsCOMPtr<nsIPrincipal> channelPrincipal;
    97   if (secMan) {
    98     secMan->GetChannelPrincipal(channel, getter_AddRefs(channelPrincipal));
    99   }
   101   int16_t decision = nsIContentPolicy::ACCEPT;
   102   nsresult rv = NS_CheckContentProcessPolicy(nsIContentPolicy::TYPE_IMAGE,
   103                                              channelURI,
   104                                              channelPrincipal,
   105                                              domWindow->GetFrameElementInternal(),
   106                                              mimeType,
   107                                              nullptr,
   108                                              &decision,
   109                                              nsContentUtils::GetContentPolicy(),
   110                                              secMan);
   112   if (NS_FAILED(rv) || NS_CP_REJECTED(decision)) {
   113     request->Cancel(NS_ERROR_CONTENT_BLOCKED);
   114     return NS_OK;
   115   }
   117   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(imgDoc->mImageContent);
   118   NS_ENSURE_TRUE(imageLoader, NS_ERROR_UNEXPECTED);
   120   imageLoader->AddObserver(imgDoc);
   121   imgDoc->mObservingImageLoader = true;
   122   imageLoader->LoadImageWithChannel(channel, getter_AddRefs(mNextStream));
   124   return MediaDocumentStreamListener::OnStartRequest(request, ctxt);
   125 }
   127 NS_IMETHODIMP
   128 ImageListener::OnStopRequest(nsIRequest* aRequest, nsISupports* aCtxt, nsresult aStatus)
   129 {
   130   ImageDocument* imgDoc = static_cast<ImageDocument*>(mDocument.get());
   131   nsContentUtils::DispatchChromeEvent(imgDoc, static_cast<nsIDocument*>(imgDoc),
   132                                       NS_LITERAL_STRING("ImageContentLoaded"),
   133                                       true, true);
   134   return MediaDocumentStreamListener::OnStopRequest(aRequest, aCtxt, aStatus);
   135 }
   137 ImageDocument::ImageDocument()
   138   : MediaDocument(),
   139     mOriginalZoomLevel(1.0)
   140 {
   141   // NOTE! nsDocument::operator new() zeroes out all members, so don't
   142   // bother initializing members to 0.
   143 }
   145 ImageDocument::~ImageDocument()
   146 {
   147 }
   150 NS_IMPL_CYCLE_COLLECTION_INHERITED(ImageDocument, MediaDocument,
   151                                    mImageContent)
   153 NS_IMPL_ADDREF_INHERITED(ImageDocument, MediaDocument)
   154 NS_IMPL_RELEASE_INHERITED(ImageDocument, MediaDocument)
   156 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(ImageDocument)
   157   NS_INTERFACE_TABLE_INHERITED(ImageDocument, nsIImageDocument,
   158                                imgINotificationObserver, nsIDOMEventListener)
   159 NS_INTERFACE_TABLE_TAIL_INHERITING(MediaDocument)
   162 nsresult
   163 ImageDocument::Init()
   164 {
   165   nsresult rv = MediaDocument::Init();
   166   NS_ENSURE_SUCCESS(rv, rv);
   168   mResizeImageByDefault = Preferences::GetBool(AUTOMATIC_IMAGE_RESIZING_PREF);
   169   mClickResizingEnabled = Preferences::GetBool(CLICK_IMAGE_RESIZING_PREF);
   170   mShouldResize = mResizeImageByDefault;
   171   mFirstResize = true;
   173   return NS_OK;
   174 }
   176 JSObject*
   177 ImageDocument::WrapNode(JSContext* aCx)
   178 {
   179   return ImageDocumentBinding::Wrap(aCx, this);
   180 }
   182 nsresult
   183 ImageDocument::StartDocumentLoad(const char*         aCommand,
   184                                  nsIChannel*         aChannel,
   185                                  nsILoadGroup*       aLoadGroup,
   186                                  nsISupports*        aContainer,
   187                                  nsIStreamListener** aDocListener,
   188                                  bool                aReset,
   189                                  nsIContentSink*     aSink)
   190 {
   191   nsresult rv =
   192     MediaDocument::StartDocumentLoad(aCommand, aChannel, aLoadGroup, aContainer,
   193                                      aDocListener, aReset, aSink);
   194   if (NS_FAILED(rv)) {
   195     return rv;
   196   }
   198   mOriginalZoomLevel =
   199     Preferences::GetBool(SITE_SPECIFIC_ZOOM, false) ? 1.0 : GetZoomLevel();
   201   NS_ASSERTION(aDocListener, "null aDocListener");
   202   *aDocListener = new ImageListener(this);
   203   NS_ADDREF(*aDocListener);
   205   return NS_OK;
   206 }
   208 void
   209 ImageDocument::Destroy()
   210 {
   211   if (mImageContent) {
   212     // Remove our event listener from the image content.
   213     nsCOMPtr<EventTarget> target = do_QueryInterface(mImageContent);
   214     target->RemoveEventListener(NS_LITERAL_STRING("load"), this, false);
   215     target->RemoveEventListener(NS_LITERAL_STRING("click"), this, false);
   217     // Break reference cycle with mImageContent, if we have one
   218     if (mObservingImageLoader) {
   219       nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mImageContent);
   220       if (imageLoader) {
   221         imageLoader->RemoveObserver(this);
   222       }
   223     }
   225     mImageContent = nullptr;
   226   }
   228   MediaDocument::Destroy();
   229 }
   231 void
   232 ImageDocument::SetScriptGlobalObject(nsIScriptGlobalObject* aScriptGlobalObject)
   233 {
   234   // If the script global object is changing, we need to unhook our event
   235   // listeners on the window.
   236   nsCOMPtr<EventTarget> target;
   237   if (mScriptGlobalObject &&
   238       aScriptGlobalObject != mScriptGlobalObject) {
   239     target = do_QueryInterface(mScriptGlobalObject);
   240     target->RemoveEventListener(NS_LITERAL_STRING("resize"), this, false);
   241     target->RemoveEventListener(NS_LITERAL_STRING("keypress"), this,
   242                                 false);
   243   }
   245   // Set the script global object on the superclass before doing
   246   // anything that might require it....
   247   MediaDocument::SetScriptGlobalObject(aScriptGlobalObject);
   249   if (aScriptGlobalObject) {
   250     if (!GetRootElement()) {
   251       // Create synthetic document
   252 #ifdef DEBUG
   253       nsresult rv =
   254 #endif
   255         CreateSyntheticDocument();
   256       NS_ASSERTION(NS_SUCCEEDED(rv), "failed to create synthetic document");
   258       target = do_QueryInterface(mImageContent);
   259       target->AddEventListener(NS_LITERAL_STRING("load"), this, false);
   260       target->AddEventListener(NS_LITERAL_STRING("click"), this, false);
   261     }
   263     target = do_QueryInterface(aScriptGlobalObject);
   264     target->AddEventListener(NS_LITERAL_STRING("resize"), this, false);
   265     target->AddEventListener(NS_LITERAL_STRING("keypress"), this, false);
   267     if (GetReadyStateEnum() != nsIDocument::READYSTATE_COMPLETE) {
   268       LinkStylesheet(NS_LITERAL_STRING("resource://gre/res/ImageDocument.css"));
   269       if (!nsContentUtils::IsChildOfSameType(this)) {
   270         LinkStylesheet(NS_LITERAL_STRING("resource://gre/res/TopLevelImageDocument.css"));
   271         LinkStylesheet(NS_LITERAL_STRING("chrome://global/skin/media/TopLevelImageDocument.css"));
   272       }
   273     }
   274     BecomeInteractive();
   275   }
   276 }
   278 void
   279 ImageDocument::OnPageShow(bool aPersisted,
   280                           EventTarget* aDispatchStartTarget)
   281 {
   282   if (aPersisted) {
   283     mOriginalZoomLevel =
   284       Preferences::GetBool(SITE_SPECIFIC_ZOOM, false) ? 1.0 : GetZoomLevel();
   285   }
   286   MediaDocument::OnPageShow(aPersisted, aDispatchStartTarget);
   287 }
   289 NS_IMETHODIMP
   290 ImageDocument::GetImageResizingEnabled(bool* aImageResizingEnabled)
   291 {
   292   *aImageResizingEnabled = ImageResizingEnabled();
   293   return NS_OK;
   294 }
   296 NS_IMETHODIMP
   297 ImageDocument::GetImageIsOverflowing(bool* aImageIsOverflowing)
   298 {
   299   *aImageIsOverflowing = ImageIsOverflowing();
   300   return NS_OK;
   301 }
   303 NS_IMETHODIMP
   304 ImageDocument::GetImageIsResized(bool* aImageIsResized)
   305 {
   306   *aImageIsResized = ImageIsResized();
   307   return NS_OK;
   308 }
   310 already_AddRefed<imgIRequest>
   311 ImageDocument::GetImageRequest(ErrorResult& aRv)
   312 {
   313   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mImageContent);
   314   nsCOMPtr<imgIRequest> imageRequest;
   315   if (imageLoader) {
   316     aRv = imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
   317                                   getter_AddRefs(imageRequest));
   318   }
   319   return imageRequest.forget();
   320 }
   322 NS_IMETHODIMP
   323 ImageDocument::GetImageRequest(imgIRequest** aImageRequest)
   324 {
   325   ErrorResult rv;
   326   *aImageRequest = GetImageRequest(rv).take();
   327   return rv.ErrorCode();
   328 }
   330 void
   331 ImageDocument::ShrinkToFit()
   332 {
   333   if (!mImageContent) {
   334     return;
   335   }
   336   if (GetZoomLevel() != mOriginalZoomLevel && mImageIsResized &&
   337       !nsContentUtils::IsChildOfSameType(this)) {
   338     return;
   339   }
   341   // Keep image content alive while changing the attributes.
   342   nsCOMPtr<nsIContent> imageContent = mImageContent;
   343   nsCOMPtr<nsIDOMHTMLImageElement> image = do_QueryInterface(mImageContent);
   344   image->SetWidth(std::max(1, NSToCoordFloor(GetRatio() * mImageWidth)));
   345   image->SetHeight(std::max(1, NSToCoordFloor(GetRatio() * mImageHeight)));
   347   // The view might have been scrolled when zooming in, scroll back to the
   348   // origin now that we're showing a shrunk-to-window version.
   349   ScrollImageTo(0, 0, false);
   351   if (!mImageContent) {
   352     // ScrollImageTo flush destroyed our content.
   353     return;
   354   }
   356   SetModeClass(eShrinkToFit);
   358   mImageIsResized = true;
   360   UpdateTitleAndCharset();
   361 }
   363 NS_IMETHODIMP
   364 ImageDocument::DOMShrinkToFit()
   365 {
   366   ShrinkToFit();
   367   return NS_OK;
   368 }
   370 NS_IMETHODIMP
   371 ImageDocument::DOMRestoreImageTo(int32_t aX, int32_t aY)
   372 {
   373   RestoreImageTo(aX, aY);
   374   return NS_OK;
   375 }
   377 void
   378 ImageDocument::ScrollImageTo(int32_t aX, int32_t aY, bool restoreImage)
   379 {
   380   float ratio = GetRatio();
   382   if (restoreImage) {
   383     RestoreImage();
   384     FlushPendingNotifications(Flush_Layout);
   385   }
   387   nsCOMPtr<nsIPresShell> shell = GetShell();
   388   if (!shell)
   389     return;
   391   nsIScrollableFrame* sf = shell->GetRootScrollFrameAsScrollable();
   392   if (!sf)
   393     return;
   395   nsRect portRect = sf->GetScrollPortRect();
   396   sf->ScrollTo(nsPoint(nsPresContext::CSSPixelsToAppUnits(aX/ratio) - portRect.width/2,
   397                        nsPresContext::CSSPixelsToAppUnits(aY/ratio) - portRect.height/2),
   398                nsIScrollableFrame::INSTANT);
   399 }
   401 void
   402 ImageDocument::RestoreImage()
   403 {
   404   if (!mImageContent) {
   405     return;
   406   }
   407   // Keep image content alive while changing the attributes.
   408   nsCOMPtr<nsIContent> imageContent = mImageContent;
   409   imageContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::width, true);
   410   imageContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::height, true);
   412   if (mImageIsOverflowing) {
   413     SetModeClass(eOverflowing);
   414   }
   415   else {
   416     SetModeClass(eNone);
   417   }
   419   mImageIsResized = false;
   421   UpdateTitleAndCharset();
   422 }
   424 NS_IMETHODIMP
   425 ImageDocument::DOMRestoreImage()
   426 {
   427   RestoreImage();
   428   return NS_OK;
   429 }
   431 void
   432 ImageDocument::ToggleImageSize()
   433 {
   434   mShouldResize = true;
   435   if (mImageIsResized) {
   436     mShouldResize = false;
   437     ResetZoomLevel();
   438     RestoreImage();
   439   }
   440   else if (mImageIsOverflowing) {
   441     ResetZoomLevel();
   442     ShrinkToFit();
   443   }
   444 }
   446 NS_IMETHODIMP
   447 ImageDocument::DOMToggleImageSize()
   448 {
   449   ToggleImageSize();
   450   return NS_OK;
   451 }
   453 NS_IMETHODIMP
   454 ImageDocument::Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aData)
   455 {
   456   if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
   457     nsCOMPtr<imgIContainer> image;
   458     aRequest->GetImage(getter_AddRefs(image));
   459     return OnStartContainer(aRequest, image);
   460   }
   462   nsDOMTokenList* classList = mImageContent->AsElement()->GetClassList();
   463   mozilla::ErrorResult rv;
   464   if (aType == imgINotificationObserver::DECODE_COMPLETE) {
   465     if (mImageContent && !nsContentUtils::IsChildOfSameType(this)) {
   466       // Update the background-color of the image only after the
   467       // image has been decoded to prevent flashes of just the
   468       // background-color.
   469       classList->Add(NS_LITERAL_STRING("decoded"), rv);
   470       NS_ENSURE_SUCCESS(rv.ErrorCode(), rv.ErrorCode());
   471     }
   472   }
   474   if (aType == imgINotificationObserver::DISCARD) {
   475     // mImageContent can be null if the document is already destroyed
   476     if (mImageContent && !nsContentUtils::IsChildOfSameType(this)) {
   477       // Remove any decoded-related styling when the image is unloaded.
   478       classList->Remove(NS_LITERAL_STRING("decoded"), rv);
   479       NS_ENSURE_SUCCESS(rv.ErrorCode(), rv.ErrorCode());
   480     }
   481   }
   483   if (aType == imgINotificationObserver::LOAD_COMPLETE) {
   484     uint32_t reqStatus;
   485     aRequest->GetImageStatus(&reqStatus);
   486     nsresult status =
   487         reqStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK;
   488     return OnStopRequest(aRequest, status);
   489   }
   491   return NS_OK;
   492 }
   494 void
   495 ImageDocument::SetModeClass(eModeClasses mode)
   496 {
   497   nsDOMTokenList* classList = mImageContent->AsElement()->GetClassList();
   498   mozilla::ErrorResult rv;
   500   if (mode == eShrinkToFit) {
   501     classList->Add(NS_LITERAL_STRING("shrinkToFit"), rv);
   502   } else {
   503     classList->Remove(NS_LITERAL_STRING("shrinkToFit"), rv);
   504   }
   506   if (mode == eOverflowing) {
   507     classList->Add(NS_LITERAL_STRING("overflowing"), rv);
   508   } else {
   509     classList->Remove(NS_LITERAL_STRING("overflowing"), rv);
   510   }
   511 }
   513 nsresult
   514 ImageDocument::OnStartContainer(imgIRequest* aRequest, imgIContainer* aImage)
   515 {
   516   // Styles have not yet been applied, so we don't know the final size. For now,
   517   // default to the image's intrinsic size.
   518   aImage->GetWidth(&mImageWidth);
   519   aImage->GetHeight(&mImageHeight);
   521   nsCOMPtr<nsIRunnable> runnable =
   522     NS_NewRunnableMethod(this, &ImageDocument::DefaultCheckOverflowing);
   523   nsContentUtils::AddScriptRunner(runnable);
   524   UpdateTitleAndCharset();
   526   return NS_OK;
   527 }
   529 nsresult
   530 ImageDocument::OnStopRequest(imgIRequest *aRequest,
   531                              nsresult aStatus)
   532 {
   533   UpdateTitleAndCharset();
   535   // mImageContent can be null if the document is already destroyed
   536   if (NS_FAILED(aStatus) && mStringBundle && mImageContent) {
   537     nsAutoCString src;
   538     mDocumentURI->GetSpec(src);
   539     NS_ConvertUTF8toUTF16 srcString(src);
   540     const char16_t* formatString[] = { srcString.get() };
   541     nsXPIDLString errorMsg;
   542     NS_NAMED_LITERAL_STRING(str, "InvalidImage");
   543     mStringBundle->FormatStringFromName(str.get(), formatString, 1,
   544                                         getter_Copies(errorMsg));
   546     mImageContent->SetAttr(kNameSpaceID_None, nsGkAtoms::alt, errorMsg, false);
   547   }
   549   return NS_OK;
   550 }
   552 NS_IMETHODIMP
   553 ImageDocument::HandleEvent(nsIDOMEvent* aEvent)
   554 {
   555   nsAutoString eventType;
   556   aEvent->GetType(eventType);
   557   if (eventType.EqualsLiteral("resize")) {
   558     CheckOverflowing(false);
   559   }
   560   else if (eventType.EqualsLiteral("click") && mClickResizingEnabled) {
   561     ResetZoomLevel();
   562     mShouldResize = true;
   563     if (mImageIsResized) {
   564       int32_t x = 0, y = 0;
   565       nsCOMPtr<nsIDOMMouseEvent> event(do_QueryInterface(aEvent));
   566       if (event) {
   567         event->GetClientX(&x);
   568         event->GetClientY(&y);
   569         int32_t left = 0, top = 0;
   570         nsCOMPtr<nsIDOMHTMLElement> htmlElement =
   571           do_QueryInterface(mImageContent);
   572         htmlElement->GetOffsetLeft(&left);
   573         htmlElement->GetOffsetTop(&top);
   574         x -= left;
   575         y -= top;
   576       }
   577       mShouldResize = false;
   578       RestoreImageTo(x, y);
   579     }
   580     else if (mImageIsOverflowing) {
   581       ShrinkToFit();
   582     }
   583   } else if (eventType.EqualsLiteral("load")) {
   584     UpdateSizeFromLayout();
   585   }
   587   return NS_OK;
   588 }
   590 void
   591 ImageDocument::UpdateSizeFromLayout()
   592 {
   593   // Pull an updated size from the content frame to account for any size
   594   // change due to CSS properties like |image-orientation|.
   595   Element* contentElement = mImageContent->AsElement();
   596   if (!contentElement) {
   597     return;
   598   }
   600   nsIFrame* contentFrame = contentElement->GetPrimaryFrame(Flush_Frames);
   601   if (!contentFrame) {
   602     return;
   603   }
   605   nsIntSize oldSize(mImageWidth, mImageHeight);
   606   IntrinsicSize newSize = contentFrame->GetIntrinsicSize();
   608   if (newSize.width.GetUnit() == eStyleUnit_Coord) {
   609     mImageWidth = nsPresContext::AppUnitsToFloatCSSPixels(newSize.width.GetCoordValue());
   610   }
   611   if (newSize.height.GetUnit() == eStyleUnit_Coord) {
   612     mImageHeight = nsPresContext::AppUnitsToFloatCSSPixels(newSize.height.GetCoordValue());
   613   }
   615   // Ensure that our information about overflow is up-to-date if needed.
   616   if (mImageWidth != oldSize.width || mImageHeight != oldSize.height) {
   617     CheckOverflowing(false);
   618   }
   619 }
   621 nsresult
   622 ImageDocument::CreateSyntheticDocument()
   623 {
   624   // Synthesize an html document that refers to the image
   625   nsresult rv = MediaDocument::CreateSyntheticDocument();
   626   NS_ENSURE_SUCCESS(rv, rv);
   628   // Add the image element
   629   Element* body = GetBodyElement();
   630   if (!body) {
   631     NS_WARNING("no body on image document!");
   632     return NS_ERROR_FAILURE;
   633   }
   635   nsCOMPtr<nsINodeInfo> nodeInfo;
   636   nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::img, nullptr,
   637                                            kNameSpaceID_XHTML,
   638                                            nsIDOMNode::ELEMENT_NODE);
   640   mImageContent = NS_NewHTMLImageElement(nodeInfo.forget());
   641   if (!mImageContent) {
   642     return NS_ERROR_OUT_OF_MEMORY;
   643   }
   644   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mImageContent);
   645   NS_ENSURE_TRUE(imageLoader, NS_ERROR_UNEXPECTED);
   647   nsAutoCString src;
   648   mDocumentURI->GetSpec(src);
   650   NS_ConvertUTF8toUTF16 srcString(src);
   651   // Make sure not to start the image load from here...
   652   imageLoader->SetLoadingEnabled(false);
   653   mImageContent->SetAttr(kNameSpaceID_None, nsGkAtoms::src, srcString, false);
   654   mImageContent->SetAttr(kNameSpaceID_None, nsGkAtoms::alt, srcString, false);
   656   body->AppendChildTo(mImageContent, false);
   657   imageLoader->SetLoadingEnabled(true);
   659   return NS_OK;
   660 }
   662 nsresult
   663 ImageDocument::CheckOverflowing(bool changeState)
   664 {
   665   /* Create a scope so that the style context gets destroyed before we might
   666    * call RebuildStyleData.  Also, holding onto pointers to the
   667    * presentation through style resolution is potentially dangerous.
   668    */
   669   {
   670     nsIPresShell *shell = GetShell();
   671     if (!shell) {
   672       return NS_OK;
   673     }
   675     nsPresContext *context = shell->GetPresContext();
   676     nsRect visibleArea = context->GetVisibleArea();
   678     mVisibleWidth = nsPresContext::AppUnitsToFloatCSSPixels(visibleArea.width);
   679     mVisibleHeight = nsPresContext::AppUnitsToFloatCSSPixels(visibleArea.height);
   680   }
   682   bool imageWasOverflowing = mImageIsOverflowing;
   683   mImageIsOverflowing =
   684     mImageWidth > mVisibleWidth || mImageHeight > mVisibleHeight;
   685   bool windowBecameBigEnough = imageWasOverflowing && !mImageIsOverflowing;
   687   if (changeState || mShouldResize || mFirstResize ||
   688       windowBecameBigEnough) {
   689     if (mImageIsOverflowing && (changeState || mShouldResize)) {
   690       ShrinkToFit();
   691     }
   692     else if (mImageIsResized || mFirstResize || windowBecameBigEnough) {
   693       RestoreImage();
   694     }
   695   }
   696   mFirstResize = false;
   698   return NS_OK;
   699 }
   701 void 
   702 ImageDocument::UpdateTitleAndCharset()
   703 {
   704   nsAutoCString typeStr;
   705   nsCOMPtr<imgIRequest> imageRequest;
   706   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mImageContent);
   707   if (imageLoader) {
   708     imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
   709                             getter_AddRefs(imageRequest));
   710   }
   712   if (imageRequest) {
   713     nsXPIDLCString mimeType;
   714     imageRequest->GetMimeType(getter_Copies(mimeType));
   715     ToUpperCase(mimeType);
   716     nsXPIDLCString::const_iterator start, end;
   717     mimeType.BeginReading(start);
   718     mimeType.EndReading(end);
   719     nsXPIDLCString::const_iterator iter = end;
   720     if (FindInReadable(NS_LITERAL_CSTRING("IMAGE/"), start, iter) && 
   721         iter != end) {
   722       // strip out "X-" if any
   723       if (*iter == 'X') {
   724         ++iter;
   725         if (iter != end && *iter == '-') {
   726           ++iter;
   727           if (iter == end) {
   728             // looks like "IMAGE/X-" is the type??  Bail out of here.
   729             mimeType.BeginReading(iter);
   730           }
   731         } else {
   732           --iter;
   733         }
   734       }
   735       typeStr = Substring(iter, end);
   736     } else {
   737       typeStr = mimeType;
   738     }
   739   }
   741   nsXPIDLString status;
   742   if (mImageIsResized) {
   743     nsAutoString ratioStr;
   744     ratioStr.AppendInt(NSToCoordFloor(GetRatio() * 100));
   746     const char16_t* formatString[1] = { ratioStr.get() };
   747     mStringBundle->FormatStringFromName(MOZ_UTF16("ScaledImage"),
   748                                         formatString, 1,
   749                                         getter_Copies(status));
   750   }
   752   static const char* const formatNames[4] = 
   753   {
   754     "ImageTitleWithNeitherDimensionsNorFile",
   755     "ImageTitleWithoutDimensions",
   756     "ImageTitleWithDimensions2",
   757     "ImageTitleWithDimensions2AndFile",
   758   };
   760   MediaDocument::UpdateTitleAndCharset(typeStr, formatNames,
   761                                        mImageWidth, mImageHeight, status);
   762 }
   764 void
   765 ImageDocument::ResetZoomLevel()
   766 {
   767   nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
   768   if (docShell) {
   769     if (nsContentUtils::IsChildOfSameType(this)) {
   770       return;
   771     }
   773     nsCOMPtr<nsIContentViewer> cv;
   774     docShell->GetContentViewer(getter_AddRefs(cv));
   775     nsCOMPtr<nsIMarkupDocumentViewer> mdv = do_QueryInterface(cv);
   776     if (mdv) {
   777       mdv->SetFullZoom(mOriginalZoomLevel);
   778     }
   779   }
   780 }
   782 float
   783 ImageDocument::GetZoomLevel()
   784 {
   785   float zoomLevel = mOriginalZoomLevel;
   786   nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
   787   if (docShell) {
   788     nsCOMPtr<nsIContentViewer> cv;
   789     docShell->GetContentViewer(getter_AddRefs(cv));
   790     nsCOMPtr<nsIMarkupDocumentViewer> mdv = do_QueryInterface(cv);
   791     if (mdv) {
   792       mdv->GetFullZoom(&zoomLevel);
   793     }
   794   }
   795   return zoomLevel;
   796 }
   798 } // namespace dom
   799 } // namespace mozilla
   801 nsresult
   802 NS_NewImageDocument(nsIDocument** aResult)
   803 {
   804   mozilla::dom::ImageDocument* doc = new mozilla::dom::ImageDocument();
   805   NS_ADDREF(doc);
   807   nsresult rv = doc->Init();
   808   if (NS_FAILED(rv)) {
   809     NS_RELEASE(doc);
   810   }
   812   *aResult = doc;
   814   return rv;
   815 }

mercurial