LCOV - code coverage report
Current view: top level - dom/html - ImageDocument.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 422 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 45 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "ImageDocument.h"
       8             : #include "mozilla/dom/ImageDocumentBinding.h"
       9             : #include "mozilla/dom/HTMLImageElement.h"
      10             : #include "nsRect.h"
      11             : #include "nsIImageLoadingContent.h"
      12             : #include "nsGenericHTMLElement.h"
      13             : #include "nsDocShell.h"
      14             : #include "nsIDocumentInlines.h"
      15             : #include "nsDOMTokenList.h"
      16             : #include "nsIDOMHTMLImageElement.h"
      17             : #include "nsIDOMEvent.h"
      18             : #include "nsIDOMKeyEvent.h"
      19             : #include "nsIDOMMouseEvent.h"
      20             : #include "nsIDOMEventListener.h"
      21             : #include "nsIFrame.h"
      22             : #include "nsGkAtoms.h"
      23             : #include "imgIRequest.h"
      24             : #include "imgILoader.h"
      25             : #include "imgIContainer.h"
      26             : #include "imgINotificationObserver.h"
      27             : #include "nsIPresShell.h"
      28             : #include "nsPresContext.h"
      29             : #include "nsStyleContext.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 "nsThreadUtils.h"
      41             : #include "nsIScrollableFrame.h"
      42             : #include "nsContentUtils.h"
      43             : #include "mozilla/dom/Element.h"
      44             : #include "mozilla/Preferences.h"
      45             : #include <algorithm>
      46             : 
      47             : #define AUTOMATIC_IMAGE_RESIZING_PREF "browser.enable_automatic_image_resizing"
      48             : #define CLICK_IMAGE_RESIZING_PREF "browser.enable_click_image_resizing"
      49             : 
      50             : //XXX A hack needed for Firefox's site specific zoom.
      51           0 : static bool IsSiteSpecific()
      52             : {
      53           0 :   return !mozilla::Preferences::GetBool("privacy.resistFingerprinting", false) &&
      54           0 :          mozilla::Preferences::GetBool("browser.zoom.siteSpecific", false);
      55             : }
      56             : 
      57             : namespace mozilla {
      58             : namespace dom {
      59             : 
      60             : class ImageListener : public MediaDocumentStreamListener
      61             : {
      62             : public:
      63             :   NS_DECL_NSIREQUESTOBSERVER
      64             : 
      65             :   explicit ImageListener(ImageDocument* aDocument);
      66             :   virtual ~ImageListener();
      67             : };
      68             : 
      69           0 : ImageListener::ImageListener(ImageDocument* aDocument)
      70           0 :   : MediaDocumentStreamListener(aDocument)
      71             : {
      72           0 : }
      73             : 
      74           0 : ImageListener::~ImageListener()
      75             : {
      76           0 : }
      77             : 
      78             : NS_IMETHODIMP
      79           0 : ImageListener::OnStartRequest(nsIRequest* request, nsISupports *ctxt)
      80             : {
      81           0 :   NS_ENSURE_TRUE(mDocument, NS_ERROR_FAILURE);
      82             : 
      83           0 :   ImageDocument *imgDoc = static_cast<ImageDocument*>(mDocument.get());
      84           0 :   nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
      85           0 :   if (!channel) {
      86           0 :     return NS_ERROR_FAILURE;
      87             :   }
      88             : 
      89           0 :   nsCOMPtr<nsPIDOMWindowOuter> domWindow = imgDoc->GetWindow();
      90           0 :   NS_ENSURE_TRUE(domWindow, NS_ERROR_UNEXPECTED);
      91             : 
      92             :   // Do a ShouldProcess check to see whether to keep loading the image.
      93           0 :   nsCOMPtr<nsIURI> channelURI;
      94           0 :   channel->GetURI(getter_AddRefs(channelURI));
      95             : 
      96           0 :   nsAutoCString mimeType;
      97           0 :   channel->GetContentType(mimeType);
      98             : 
      99           0 :   nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
     100           0 :   nsCOMPtr<nsIPrincipal> channelPrincipal;
     101           0 :   if (secMan) {
     102           0 :     secMan->GetChannelResultPrincipal(channel, getter_AddRefs(channelPrincipal));
     103             :   }
     104             : 
     105           0 :   int16_t decision = nsIContentPolicy::ACCEPT;
     106           0 :   nsresult rv = NS_CheckContentProcessPolicy(nsIContentPolicy::TYPE_INTERNAL_IMAGE,
     107             :                                              channelURI,
     108             :                                              channelPrincipal,
     109           0 :                                              domWindow->GetFrameElementInternal(),
     110             :                                              mimeType,
     111             :                                              nullptr,
     112             :                                              &decision,
     113             :                                              nsContentUtils::GetContentPolicy(),
     114           0 :                                              secMan);
     115             : 
     116           0 :   if (NS_FAILED(rv) || NS_CP_REJECTED(decision)) {
     117           0 :     request->Cancel(NS_ERROR_CONTENT_BLOCKED);
     118           0 :     return NS_OK;
     119             :   }
     120             : 
     121           0 :   if (!imgDoc->mObservingImageLoader) {
     122           0 :     nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(imgDoc->mImageContent);
     123           0 :     NS_ENSURE_TRUE(imageLoader, NS_ERROR_UNEXPECTED);
     124             : 
     125           0 :     imageLoader->AddObserver(imgDoc);
     126           0 :     imgDoc->mObservingImageLoader = true;
     127           0 :     imageLoader->LoadImageWithChannel(channel, getter_AddRefs(mNextStream));
     128             :   }
     129             : 
     130           0 :   return MediaDocumentStreamListener::OnStartRequest(request, ctxt);
     131             : }
     132             : 
     133             : NS_IMETHODIMP
     134           0 : ImageListener::OnStopRequest(nsIRequest* aRequest, nsISupports* aCtxt, nsresult aStatus)
     135             : {
     136           0 :   ImageDocument* imgDoc = static_cast<ImageDocument*>(mDocument.get());
     137           0 :   nsContentUtils::DispatchChromeEvent(imgDoc, static_cast<nsIDocument*>(imgDoc),
     138           0 :                                       NS_LITERAL_STRING("ImageContentLoaded"),
     139           0 :                                       true, true);
     140           0 :   return MediaDocumentStreamListener::OnStopRequest(aRequest, aCtxt, aStatus);
     141             : }
     142             : 
     143           0 : ImageDocument::ImageDocument()
     144             :   : MediaDocument()
     145             :   , mVisibleWidth(0.0)
     146             :   , mVisibleHeight(0.0)
     147             :   , mImageWidth(0)
     148             :   , mImageHeight(0)
     149             :   , mResizeImageByDefault(false)
     150             :   , mClickResizingEnabled(false)
     151             :   , mImageIsOverflowingHorizontally(false)
     152             :   , mImageIsOverflowingVertically(false)
     153             :   , mImageIsResized(false)
     154             :   , mShouldResize(false)
     155             :   , mFirstResize(false)
     156             :   , mObservingImageLoader(false)
     157           0 :   , mOriginalZoomLevel(1.0)
     158             : {
     159           0 : }
     160             : 
     161           0 : ImageDocument::~ImageDocument()
     162             : {
     163           0 : }
     164             : 
     165             : 
     166           0 : NS_IMPL_CYCLE_COLLECTION_INHERITED(ImageDocument, MediaDocument,
     167             :                                    mImageContent)
     168             : 
     169           0 : NS_IMPL_ADDREF_INHERITED(ImageDocument, MediaDocument)
     170           0 : NS_IMPL_RELEASE_INHERITED(ImageDocument, MediaDocument)
     171             : 
     172           0 : NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(ImageDocument)
     173           0 :   NS_INTERFACE_TABLE_INHERITED(ImageDocument, nsIImageDocument,
     174             :                                imgINotificationObserver, nsIDOMEventListener)
     175           0 : NS_INTERFACE_TABLE_TAIL_INHERITING(MediaDocument)
     176             : 
     177             : 
     178             : nsresult
     179           0 : ImageDocument::Init()
     180             : {
     181           0 :   nsresult rv = MediaDocument::Init();
     182           0 :   NS_ENSURE_SUCCESS(rv, rv);
     183             : 
     184           0 :   mResizeImageByDefault = Preferences::GetBool(AUTOMATIC_IMAGE_RESIZING_PREF);
     185           0 :   mClickResizingEnabled = Preferences::GetBool(CLICK_IMAGE_RESIZING_PREF);
     186           0 :   mShouldResize = mResizeImageByDefault;
     187           0 :   mFirstResize = true;
     188             : 
     189           0 :   return NS_OK;
     190             : }
     191             : 
     192             : JSObject*
     193           0 : ImageDocument::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
     194             : {
     195           0 :   return ImageDocumentBinding::Wrap(aCx, this, aGivenProto);
     196             : }
     197             : 
     198             : nsresult
     199           0 : ImageDocument::StartDocumentLoad(const char*         aCommand,
     200             :                                  nsIChannel*         aChannel,
     201             :                                  nsILoadGroup*       aLoadGroup,
     202             :                                  nsISupports*        aContainer,
     203             :                                  nsIStreamListener** aDocListener,
     204             :                                  bool                aReset,
     205             :                                  nsIContentSink*     aSink)
     206             : {
     207             :   nsresult rv =
     208           0 :     MediaDocument::StartDocumentLoad(aCommand, aChannel, aLoadGroup, aContainer,
     209           0 :                                      aDocListener, aReset, aSink);
     210           0 :   if (NS_FAILED(rv)) {
     211           0 :     return rv;
     212             :   }
     213             : 
     214           0 :   mOriginalZoomLevel = IsSiteSpecific() ? 1.0 : GetZoomLevel();
     215             : 
     216           0 :   NS_ASSERTION(aDocListener, "null aDocListener");
     217           0 :   *aDocListener = new ImageListener(this);
     218           0 :   NS_ADDREF(*aDocListener);
     219             : 
     220           0 :   return NS_OK;
     221             : }
     222             : 
     223             : void
     224           0 : ImageDocument::Destroy()
     225             : {
     226           0 :   if (mImageContent) {
     227             :     // Remove our event listener from the image content.
     228           0 :     nsCOMPtr<EventTarget> target = do_QueryInterface(mImageContent);
     229           0 :     target->RemoveEventListener(NS_LITERAL_STRING("load"), this, false);
     230           0 :     target->RemoveEventListener(NS_LITERAL_STRING("click"), this, false);
     231             : 
     232             :     // Break reference cycle with mImageContent, if we have one
     233           0 :     if (mObservingImageLoader) {
     234           0 :       nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mImageContent);
     235           0 :       if (imageLoader) {
     236           0 :         imageLoader->RemoveObserver(this);
     237             :       }
     238             :     }
     239             : 
     240           0 :     mImageContent = nullptr;
     241             :   }
     242             : 
     243           0 :   MediaDocument::Destroy();
     244           0 : }
     245             : 
     246             : void
     247           0 : ImageDocument::SetScriptGlobalObject(nsIScriptGlobalObject* aScriptGlobalObject)
     248             : {
     249             :   // If the script global object is changing, we need to unhook our event
     250             :   // listeners on the window.
     251           0 :   nsCOMPtr<EventTarget> target;
     252           0 :   if (mScriptGlobalObject &&
     253           0 :       aScriptGlobalObject != mScriptGlobalObject) {
     254           0 :     target = do_QueryInterface(mScriptGlobalObject);
     255           0 :     target->RemoveEventListener(NS_LITERAL_STRING("resize"), this, false);
     256           0 :     target->RemoveEventListener(NS_LITERAL_STRING("keypress"), this,
     257           0 :                                 false);
     258             :   }
     259             : 
     260             :   // Set the script global object on the superclass before doing
     261             :   // anything that might require it....
     262           0 :   MediaDocument::SetScriptGlobalObject(aScriptGlobalObject);
     263             : 
     264           0 :   if (aScriptGlobalObject) {
     265           0 :     if (!GetRootElement()) {
     266             :       // Create synthetic document
     267             : #ifdef DEBUG
     268             :       nsresult rv =
     269             : #endif
     270           0 :         CreateSyntheticDocument();
     271           0 :       NS_ASSERTION(NS_SUCCEEDED(rv), "failed to create synthetic document");
     272             : 
     273           0 :       target = do_QueryInterface(mImageContent);
     274           0 :       target->AddEventListener(NS_LITERAL_STRING("load"), this, false);
     275           0 :       target->AddEventListener(NS_LITERAL_STRING("click"), this, false);
     276             :     }
     277             : 
     278           0 :     target = do_QueryInterface(aScriptGlobalObject);
     279           0 :     target->AddEventListener(NS_LITERAL_STRING("resize"), this, false);
     280           0 :     target->AddEventListener(NS_LITERAL_STRING("keypress"), this, false);
     281             : 
     282           0 :     if (GetReadyStateEnum() != nsIDocument::READYSTATE_COMPLETE) {
     283           0 :       LinkStylesheet(NS_LITERAL_STRING("resource://gre/res/ImageDocument.css"));
     284           0 :       if (!nsContentUtils::IsChildOfSameType(this)) {
     285           0 :         LinkStylesheet(NS_LITERAL_STRING("resource://gre/res/TopLevelImageDocument.css"));
     286           0 :         LinkStylesheet(NS_LITERAL_STRING("chrome://global/skin/media/TopLevelImageDocument.css"));
     287             :       }
     288             :     }
     289           0 :     BecomeInteractive();
     290             :   }
     291           0 : }
     292             : 
     293             : void
     294           0 : ImageDocument::OnPageShow(bool aPersisted,
     295             :                           EventTarget* aDispatchStartTarget)
     296             : {
     297           0 :   if (aPersisted) {
     298           0 :     mOriginalZoomLevel = IsSiteSpecific() ? 1.0 : GetZoomLevel();
     299             :   }
     300           0 :   RefPtr<ImageDocument> kungFuDeathGrip(this);
     301           0 :   UpdateSizeFromLayout();
     302             : 
     303           0 :   MediaDocument::OnPageShow(aPersisted, aDispatchStartTarget);
     304           0 : }
     305             : 
     306             : NS_IMETHODIMP
     307           0 : ImageDocument::GetImageIsOverflowing(bool* aImageIsOverflowing)
     308             : {
     309           0 :   *aImageIsOverflowing = ImageIsOverflowing();
     310           0 :   return NS_OK;
     311             : }
     312             : 
     313             : NS_IMETHODIMP
     314           0 : ImageDocument::GetImageIsResized(bool* aImageIsResized)
     315             : {
     316           0 :   *aImageIsResized = ImageIsResized();
     317           0 :   return NS_OK;
     318             : }
     319             : 
     320             : already_AddRefed<imgIRequest>
     321           0 : ImageDocument::GetImageRequest(ErrorResult& aRv)
     322             : {
     323           0 :   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mImageContent);
     324           0 :   nsCOMPtr<imgIRequest> imageRequest;
     325           0 :   if (imageLoader) {
     326           0 :     aRv = imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
     327           0 :                                   getter_AddRefs(imageRequest));
     328             :   }
     329           0 :   return imageRequest.forget();
     330             : }
     331             : 
     332             : NS_IMETHODIMP
     333           0 : ImageDocument::GetImageRequest(imgIRequest** aImageRequest)
     334             : {
     335           0 :   ErrorResult rv;
     336           0 :   *aImageRequest = GetImageRequest(rv).take();
     337           0 :   return rv.StealNSResult();
     338             : }
     339             : 
     340             : void
     341           0 : ImageDocument::ShrinkToFit()
     342             : {
     343           0 :   if (!mImageContent) {
     344           0 :     return;
     345             :   }
     346           0 :   if (GetZoomLevel() != mOriginalZoomLevel && mImageIsResized &&
     347           0 :       !nsContentUtils::IsChildOfSameType(this)) {
     348             :     // If we're zoomed, so that we don't maintain the invariant that
     349             :     // mImageIsResized if and only if its displayed width/height fit in
     350             :     // mVisibleWidth/mVisibleHeight, then we may need to switch to/from the
     351             :     // overflowingVertical class here, because our viewport size may have
     352             :     // changed and we don't plan to adjust the image size to compensate.  Since
     353             :     // mImageIsResized it has a "height" attribute set, and we can just get the
     354             :     // displayed image height by getting .height on the HTMLImageElement.
     355           0 :     HTMLImageElement* img = HTMLImageElement::FromContent(mImageContent);
     356           0 :     uint32_t imageHeight = img->Height();
     357           0 :     nsDOMTokenList* classList = img->ClassList();
     358           0 :     ErrorResult ignored;
     359           0 :     if (imageHeight > mVisibleHeight) {
     360           0 :       classList->Add(NS_LITERAL_STRING("overflowingVertical"), ignored);
     361             :     } else {
     362           0 :       classList->Remove(NS_LITERAL_STRING("overflowingVertical"), ignored);
     363             :     }
     364           0 :     ignored.SuppressException();
     365           0 :     return;
     366             :   }
     367             : 
     368             :   // Keep image content alive while changing the attributes.
     369           0 :   nsCOMPtr<Element> imageContent = mImageContent;
     370           0 :   nsCOMPtr<nsIDOMHTMLImageElement> image = do_QueryInterface(imageContent);
     371           0 :   image->SetWidth(std::max(1, NSToCoordFloor(GetRatio() * mImageWidth)));
     372           0 :   image->SetHeight(std::max(1, NSToCoordFloor(GetRatio() * mImageHeight)));
     373             : 
     374             :   // The view might have been scrolled when zooming in, scroll back to the
     375             :   // origin now that we're showing a shrunk-to-window version.
     376           0 :   ScrollImageTo(0, 0, false);
     377             : 
     378           0 :   if (!mImageContent) {
     379             :     // ScrollImageTo flush destroyed our content.
     380           0 :     return;
     381             :   }
     382             : 
     383           0 :   SetModeClass(eShrinkToFit);
     384             : 
     385           0 :   mImageIsResized = true;
     386             : 
     387           0 :   UpdateTitleAndCharset();
     388             : }
     389             : 
     390             : NS_IMETHODIMP
     391           0 : ImageDocument::DOMShrinkToFit()
     392             : {
     393           0 :   ShrinkToFit();
     394           0 :   return NS_OK;
     395             : }
     396             : 
     397             : NS_IMETHODIMP
     398           0 : ImageDocument::DOMRestoreImageTo(int32_t aX, int32_t aY)
     399             : {
     400           0 :   RestoreImageTo(aX, aY);
     401           0 :   return NS_OK;
     402             : }
     403             : 
     404             : void
     405           0 : ImageDocument::ScrollImageTo(int32_t aX, int32_t aY, bool restoreImage)
     406             : {
     407           0 :   float ratio = GetRatio();
     408             : 
     409           0 :   if (restoreImage) {
     410           0 :     RestoreImage();
     411           0 :     FlushPendingNotifications(FlushType::Layout);
     412             :   }
     413             : 
     414           0 :   nsCOMPtr<nsIPresShell> shell = GetShell();
     415           0 :   if (!shell) {
     416           0 :     return;
     417             :   }
     418             : 
     419           0 :   nsIScrollableFrame* sf = shell->GetRootScrollFrameAsScrollable();
     420           0 :   if (!sf) {
     421           0 :     return;
     422             :   }
     423             : 
     424           0 :   nsRect portRect = sf->GetScrollPortRect();
     425           0 :   sf->ScrollTo(nsPoint(nsPresContext::CSSPixelsToAppUnits(aX/ratio) - portRect.width/2,
     426           0 :                        nsPresContext::CSSPixelsToAppUnits(aY/ratio) - portRect.height/2),
     427           0 :                nsIScrollableFrame::INSTANT);
     428             : }
     429             : 
     430             : void
     431           0 : ImageDocument::RestoreImage()
     432             : {
     433           0 :   if (!mImageContent) {
     434           0 :     return;
     435             :   }
     436             :   // Keep image content alive while changing the attributes.
     437           0 :   nsCOMPtr<Element> imageContent = mImageContent;
     438           0 :   imageContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::width, true);
     439           0 :   imageContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::height, true);
     440             : 
     441           0 :   if (ImageIsOverflowing()) {
     442           0 :     if (!mImageIsOverflowingVertically) {
     443           0 :       SetModeClass(eOverflowingHorizontalOnly);
     444             :     } else {
     445           0 :       SetModeClass(eOverflowingVertical);
     446             :     }
     447             :   }
     448             :   else {
     449           0 :     SetModeClass(eNone);
     450             :   }
     451             : 
     452           0 :   mImageIsResized = false;
     453             : 
     454           0 :   UpdateTitleAndCharset();
     455             : }
     456             : 
     457             : NS_IMETHODIMP
     458           0 : ImageDocument::DOMRestoreImage()
     459             : {
     460           0 :   RestoreImage();
     461           0 :   return NS_OK;
     462             : }
     463             : 
     464             : void
     465           0 : ImageDocument::ToggleImageSize()
     466             : {
     467           0 :   mShouldResize = true;
     468           0 :   if (mImageIsResized) {
     469           0 :     mShouldResize = false;
     470           0 :     ResetZoomLevel();
     471           0 :     RestoreImage();
     472             :   }
     473           0 :   else if (ImageIsOverflowing()) {
     474           0 :     ResetZoomLevel();
     475           0 :     ShrinkToFit();
     476             :   }
     477           0 : }
     478             : 
     479             : NS_IMETHODIMP
     480           0 : ImageDocument::DOMToggleImageSize()
     481             : {
     482           0 :   ToggleImageSize();
     483           0 :   return NS_OK;
     484             : }
     485             : 
     486             : NS_IMETHODIMP
     487           0 : ImageDocument::Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aData)
     488             : {
     489           0 :   if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
     490           0 :     nsCOMPtr<imgIContainer> image;
     491           0 :     aRequest->GetImage(getter_AddRefs(image));
     492           0 :     return OnSizeAvailable(aRequest, image);
     493             :   }
     494             : 
     495             :   // Run this using a script runner because HAS_TRANSPARENCY notifications can
     496             :   // come during painting and this will trigger invalidation.
     497           0 :   if (aType == imgINotificationObserver::HAS_TRANSPARENCY) {
     498             :     nsCOMPtr<nsIRunnable> runnable =
     499           0 :       NewRunnableMethod("dom::ImageDocument::OnHasTransparency",
     500             :                         this,
     501           0 :                         &ImageDocument::OnHasTransparency);
     502           0 :     nsContentUtils::AddScriptRunner(runnable);
     503             :   }
     504             : 
     505           0 :   if (aType == imgINotificationObserver::LOAD_COMPLETE) {
     506             :     uint32_t reqStatus;
     507           0 :     aRequest->GetImageStatus(&reqStatus);
     508             :     nsresult status =
     509           0 :         reqStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK;
     510           0 :     return OnLoadComplete(aRequest, status);
     511             :   }
     512             : 
     513           0 :   return NS_OK;
     514             : }
     515             : 
     516             : void
     517           0 : ImageDocument::OnHasTransparency()
     518             : {
     519           0 :   if (!mImageContent || nsContentUtils::IsChildOfSameType(this)) {
     520           0 :     return;
     521             :   }
     522             : 
     523           0 :   nsDOMTokenList* classList = mImageContent->ClassList();
     524           0 :   mozilla::ErrorResult rv;
     525           0 :   classList->Add(NS_LITERAL_STRING("transparent"), rv);
     526             : }
     527             : 
     528             : void
     529           0 : ImageDocument::SetModeClass(eModeClasses mode)
     530             : {
     531           0 :   nsDOMTokenList* classList = mImageContent->ClassList();
     532           0 :   ErrorResult rv;
     533             : 
     534           0 :   if (mode == eShrinkToFit) {
     535           0 :     classList->Add(NS_LITERAL_STRING("shrinkToFit"), rv);
     536             :   } else {
     537           0 :     classList->Remove(NS_LITERAL_STRING("shrinkToFit"), rv);
     538             :   }
     539             : 
     540           0 :   if (mode == eOverflowingVertical) {
     541           0 :     classList->Add(NS_LITERAL_STRING("overflowingVertical"), rv);
     542             :   } else {
     543           0 :     classList->Remove(NS_LITERAL_STRING("overflowingVertical"), rv);
     544             :   }
     545             : 
     546           0 :   if (mode == eOverflowingHorizontalOnly) {
     547           0 :     classList->Add(NS_LITERAL_STRING("overflowingHorizontalOnly"), rv);
     548             :   } else {
     549           0 :     classList->Remove(NS_LITERAL_STRING("overflowingHorizontalOnly"), rv);
     550             :   }
     551             : 
     552           0 :   rv.SuppressException();
     553           0 : }
     554             : 
     555             : nsresult
     556           0 : ImageDocument::OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage)
     557             : {
     558           0 :   int32_t oldWidth = mImageWidth;
     559           0 :   int32_t oldHeight = mImageHeight;
     560             : 
     561             :   // Styles have not yet been applied, so we don't know the final size. For now,
     562             :   // default to the image's intrinsic size.
     563           0 :   aImage->GetWidth(&mImageWidth);
     564           0 :   aImage->GetHeight(&mImageHeight);
     565             : 
     566             :   // Multipart images send size available for each part; ignore them if it
     567             :   // doesn't change our size. (We may not even support changing size in
     568             :   // multipart images in the future.)
     569           0 :   if (oldWidth == mImageWidth && oldHeight == mImageHeight) {
     570           0 :     return NS_OK;
     571             :   }
     572             : 
     573             :   nsCOMPtr<nsIRunnable> runnable =
     574           0 :     NewRunnableMethod("dom::ImageDocument::DefaultCheckOverflowing",
     575             :                       this,
     576           0 :                       &ImageDocument::DefaultCheckOverflowing);
     577           0 :   nsContentUtils::AddScriptRunner(runnable);
     578           0 :   UpdateTitleAndCharset();
     579             : 
     580           0 :   return NS_OK;
     581             : }
     582             : 
     583             : nsresult
     584           0 : ImageDocument::OnLoadComplete(imgIRequest* aRequest, nsresult aStatus)
     585             : {
     586           0 :   UpdateTitleAndCharset();
     587             : 
     588             :   // mImageContent can be null if the document is already destroyed
     589           0 :   if (NS_FAILED(aStatus) && mStringBundle && mImageContent) {
     590           0 :     nsAutoCString src;
     591           0 :     mDocumentURI->GetSpec(src);
     592           0 :     NS_ConvertUTF8toUTF16 srcString(src);
     593           0 :     const char16_t* formatString[] = { srcString.get() };
     594           0 :     nsXPIDLString errorMsg;
     595           0 :     mStringBundle->FormatStringFromName(u"InvalidImage", formatString, 1,
     596           0 :                                         getter_Copies(errorMsg));
     597             : 
     598           0 :     mImageContent->SetAttr(kNameSpaceID_None, nsGkAtoms::alt, errorMsg, false);
     599             :   }
     600             : 
     601           0 :   return NS_OK;
     602             : }
     603             : 
     604             : NS_IMETHODIMP
     605           0 : ImageDocument::HandleEvent(nsIDOMEvent* aEvent)
     606             : {
     607           0 :   nsAutoString eventType;
     608           0 :   aEvent->GetType(eventType);
     609           0 :   if (eventType.EqualsLiteral("resize")) {
     610           0 :     CheckOverflowing(false);
     611             :   }
     612           0 :   else if (eventType.EqualsLiteral("click") && mClickResizingEnabled) {
     613           0 :     ResetZoomLevel();
     614           0 :     mShouldResize = true;
     615           0 :     if (mImageIsResized) {
     616           0 :       int32_t x = 0, y = 0;
     617           0 :       nsCOMPtr<nsIDOMMouseEvent> event(do_QueryInterface(aEvent));
     618           0 :       if (event) {
     619           0 :         event->GetClientX(&x);
     620           0 :         event->GetClientY(&y);
     621           0 :         int32_t left = 0, top = 0;
     622             :         nsCOMPtr<nsIDOMHTMLElement> htmlElement =
     623           0 :           do_QueryInterface(mImageContent);
     624           0 :         htmlElement->GetOffsetLeft(&left);
     625           0 :         htmlElement->GetOffsetTop(&top);
     626           0 :         x -= left;
     627           0 :         y -= top;
     628             :       }
     629           0 :       mShouldResize = false;
     630           0 :       RestoreImageTo(x, y);
     631             :     }
     632           0 :     else if (ImageIsOverflowing()) {
     633           0 :       ShrinkToFit();
     634             :     }
     635           0 :   } else if (eventType.EqualsLiteral("load")) {
     636           0 :     UpdateSizeFromLayout();
     637             :   }
     638             : 
     639           0 :   return NS_OK;
     640             : }
     641             : 
     642             : void
     643           0 : ImageDocument::UpdateSizeFromLayout()
     644             : {
     645             :   // Pull an updated size from the content frame to account for any size
     646             :   // change due to CSS properties like |image-orientation|.
     647           0 :   if (!mImageContent) {
     648           0 :     return;
     649             :   }
     650             : 
     651           0 :   nsIFrame* contentFrame = mImageContent->GetPrimaryFrame(FlushType::Frames);
     652           0 :   if (!contentFrame) {
     653           0 :     return;
     654             :   }
     655             : 
     656           0 :   nsIntSize oldSize(mImageWidth, mImageHeight);
     657           0 :   IntrinsicSize newSize = contentFrame->GetIntrinsicSize();
     658             : 
     659           0 :   if (newSize.width.GetUnit() == eStyleUnit_Coord) {
     660           0 :     mImageWidth = nsPresContext::AppUnitsToFloatCSSPixels(newSize.width.GetCoordValue());
     661             :   }
     662           0 :   if (newSize.height.GetUnit() == eStyleUnit_Coord) {
     663           0 :     mImageHeight = nsPresContext::AppUnitsToFloatCSSPixels(newSize.height.GetCoordValue());
     664             :   }
     665             : 
     666             :   // Ensure that our information about overflow is up-to-date if needed.
     667           0 :   if (mImageWidth != oldSize.width || mImageHeight != oldSize.height) {
     668           0 :     CheckOverflowing(false);
     669             :   }
     670             : }
     671             : 
     672             : nsresult
     673           0 : ImageDocument::CreateSyntheticDocument()
     674             : {
     675             :   // Synthesize an html document that refers to the image
     676           0 :   nsresult rv = MediaDocument::CreateSyntheticDocument();
     677           0 :   NS_ENSURE_SUCCESS(rv, rv);
     678             : 
     679             :   // Add the image element
     680           0 :   Element* body = GetBodyElement();
     681           0 :   if (!body) {
     682           0 :     NS_WARNING("no body on image document!");
     683           0 :     return NS_ERROR_FAILURE;
     684             :   }
     685             : 
     686           0 :   RefPtr<mozilla::dom::NodeInfo> nodeInfo;
     687           0 :   nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::img, nullptr,
     688             :                                            kNameSpaceID_XHTML,
     689           0 :                                            nsIDOMNode::ELEMENT_NODE);
     690             : 
     691           0 :   mImageContent = NS_NewHTMLImageElement(nodeInfo.forget());
     692           0 :   if (!mImageContent) {
     693           0 :     return NS_ERROR_OUT_OF_MEMORY;
     694             :   }
     695           0 :   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mImageContent);
     696           0 :   NS_ENSURE_TRUE(imageLoader, NS_ERROR_UNEXPECTED);
     697             : 
     698           0 :   nsAutoCString src;
     699           0 :   mDocumentURI->GetSpec(src);
     700             : 
     701           0 :   NS_ConvertUTF8toUTF16 srcString(src);
     702             :   // Make sure not to start the image load from here...
     703           0 :   imageLoader->SetLoadingEnabled(false);
     704           0 :   mImageContent->SetAttr(kNameSpaceID_None, nsGkAtoms::src, srcString, false);
     705           0 :   mImageContent->SetAttr(kNameSpaceID_None, nsGkAtoms::alt, srcString, false);
     706             : 
     707           0 :   body->AppendChildTo(mImageContent, false);
     708           0 :   imageLoader->SetLoadingEnabled(true);
     709             : 
     710           0 :   return NS_OK;
     711             : }
     712             : 
     713             : nsresult
     714           0 : ImageDocument::CheckOverflowing(bool changeState)
     715             : {
     716             :   /* Create a scope so that the style context gets destroyed before we might
     717             :    * call RebuildStyleData.  Also, holding onto pointers to the
     718             :    * presentation through style resolution is potentially dangerous.
     719             :    */
     720             :   {
     721           0 :     nsIPresShell *shell = GetShell();
     722           0 :     if (!shell) {
     723           0 :       return NS_OK;
     724             :     }
     725             : 
     726           0 :     nsPresContext *context = shell->GetPresContext();
     727           0 :     nsRect visibleArea = context->GetVisibleArea();
     728             : 
     729           0 :     mVisibleWidth = nsPresContext::AppUnitsToFloatCSSPixels(visibleArea.width);
     730           0 :     mVisibleHeight = nsPresContext::AppUnitsToFloatCSSPixels(visibleArea.height);
     731             :   }
     732             : 
     733           0 :   bool imageWasOverflowing = ImageIsOverflowing();
     734           0 :   bool imageWasOverflowingVertically = mImageIsOverflowingVertically;
     735           0 :   mImageIsOverflowingHorizontally = mImageWidth > mVisibleWidth;
     736           0 :   mImageIsOverflowingVertically = mImageHeight > mVisibleHeight;
     737           0 :   bool windowBecameBigEnough = imageWasOverflowing && !ImageIsOverflowing();
     738             :   bool verticalOverflowChanged =
     739           0 :     mImageIsOverflowingVertically != imageWasOverflowingVertically;
     740             : 
     741           0 :   if (changeState || mShouldResize || mFirstResize ||
     742           0 :       windowBecameBigEnough || verticalOverflowChanged) {
     743           0 :     if (ImageIsOverflowing() && (changeState || mShouldResize)) {
     744           0 :       ShrinkToFit();
     745             :     }
     746           0 :     else if (mImageIsResized || mFirstResize || windowBecameBigEnough) {
     747           0 :       RestoreImage();
     748           0 :     } else if (!mImageIsResized && verticalOverflowChanged) {
     749           0 :       if (mImageIsOverflowingVertically) {
     750           0 :         SetModeClass(eOverflowingVertical);
     751             :       } else {
     752           0 :         SetModeClass(eOverflowingHorizontalOnly);
     753             :       }
     754             :     }
     755             :   }
     756           0 :   mFirstResize = false;
     757             : 
     758           0 :   return NS_OK;
     759             : }
     760             : 
     761             : void
     762           0 : ImageDocument::UpdateTitleAndCharset()
     763             : {
     764           0 :   nsAutoCString typeStr;
     765           0 :   nsCOMPtr<imgIRequest> imageRequest;
     766           0 :   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mImageContent);
     767           0 :   if (imageLoader) {
     768           0 :     imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
     769           0 :                             getter_AddRefs(imageRequest));
     770             :   }
     771             : 
     772           0 :   if (imageRequest) {
     773           0 :     nsXPIDLCString mimeType;
     774           0 :     imageRequest->GetMimeType(getter_Copies(mimeType));
     775           0 :     ToUpperCase(mimeType);
     776           0 :     nsXPIDLCString::const_iterator start, end;
     777           0 :     mimeType.BeginReading(start);
     778           0 :     mimeType.EndReading(end);
     779           0 :     nsXPIDLCString::const_iterator iter = end;
     780           0 :     if (FindInReadable(NS_LITERAL_CSTRING("IMAGE/"), start, iter) &&
     781           0 :         iter != end) {
     782             :       // strip out "X-" if any
     783           0 :       if (*iter == 'X') {
     784           0 :         ++iter;
     785           0 :         if (iter != end && *iter == '-') {
     786           0 :           ++iter;
     787           0 :           if (iter == end) {
     788             :             // looks like "IMAGE/X-" is the type??  Bail out of here.
     789           0 :             mimeType.BeginReading(iter);
     790             :           }
     791             :         } else {
     792           0 :           --iter;
     793             :         }
     794             :       }
     795           0 :       typeStr = Substring(iter, end);
     796             :     } else {
     797           0 :       typeStr = mimeType;
     798             :     }
     799             :   }
     800             : 
     801           0 :   nsXPIDLString status;
     802           0 :   if (mImageIsResized) {
     803           0 :     nsAutoString ratioStr;
     804           0 :     ratioStr.AppendInt(NSToCoordFloor(GetRatio() * 100));
     805             : 
     806           0 :     const char16_t* formatString[1] = { ratioStr.get() };
     807           0 :     mStringBundle->FormatStringFromName(u"ScaledImage",
     808             :                                         formatString, 1,
     809           0 :                                         getter_Copies(status));
     810             :   }
     811             : 
     812             :   static const char* const formatNames[4] =
     813             :   {
     814             :     "ImageTitleWithNeitherDimensionsNorFile",
     815             :     "ImageTitleWithoutDimensions",
     816             :     "ImageTitleWithDimensions2",
     817             :     "ImageTitleWithDimensions2AndFile",
     818             :   };
     819             : 
     820           0 :   MediaDocument::UpdateTitleAndCharset(typeStr, mChannel, formatNames,
     821           0 :                                        mImageWidth, mImageHeight, status);
     822           0 : }
     823             : 
     824             : void
     825           0 : ImageDocument::ResetZoomLevel()
     826             : {
     827           0 :   nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
     828           0 :   if (docShell) {
     829           0 :     if (nsContentUtils::IsChildOfSameType(this)) {
     830           0 :       return;
     831             :     }
     832             : 
     833           0 :     nsCOMPtr<nsIContentViewer> cv;
     834           0 :     docShell->GetContentViewer(getter_AddRefs(cv));
     835           0 :     if (cv) {
     836           0 :       cv->SetFullZoom(mOriginalZoomLevel);
     837             :     }
     838             :   }
     839             : }
     840             : 
     841             : float
     842           0 : ImageDocument::GetZoomLevel()
     843             : {
     844           0 :   float zoomLevel = mOriginalZoomLevel;
     845           0 :   nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
     846           0 :   if (docShell) {
     847           0 :     nsCOMPtr<nsIContentViewer> cv;
     848           0 :     docShell->GetContentViewer(getter_AddRefs(cv));
     849           0 :     if (cv) {
     850           0 :       cv->GetFullZoom(&zoomLevel);
     851             :     }
     852             :   }
     853           0 :   return zoomLevel;
     854             : }
     855             : 
     856             : } // namespace dom
     857             : } // namespace mozilla
     858             : 
     859             : nsresult
     860           0 : NS_NewImageDocument(nsIDocument** aResult)
     861             : {
     862           0 :   mozilla::dom::ImageDocument* doc = new mozilla::dom::ImageDocument();
     863           0 :   NS_ADDREF(doc);
     864             : 
     865           0 :   nsresult rv = doc->Init();
     866           0 :   if (NS_FAILED(rv)) {
     867           0 :     NS_RELEASE(doc);
     868             :   }
     869             : 
     870           0 :   *aResult = doc;
     871             : 
     872           0 :   return rv;
     873             : }

Generated by: LCOV version 1.13