Bumping gaia.json for 8 gaia revision(s) a=gaia-bump
[gecko.git] / dom / html / ImageDocument.cpp
blob951caa939a2c176dc1f7cc1649d88af8f096c564
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 "nsThreadUtils.h"
41 #include "nsIScrollableFrame.h"
42 #include "nsContentUtils.h"
43 #include "mozilla/dom/Element.h"
44 #include "mozilla/Preferences.h"
45 #include <algorithm>
47 #define AUTOMATIC_IMAGE_RESIZING_PREF "browser.enable_automatic_image_resizing"
48 #define CLICK_IMAGE_RESIZING_PREF "browser.enable_click_image_resizing"
49 //XXX A hack needed for Firefox's site specific zoom.
50 #define SITE_SPECIFIC_ZOOM "browser.zoom.siteSpecific"
52 namespace mozilla {
53 namespace dom {
55 class ImageListener : public MediaDocumentStreamListener
57 public:
58 NS_DECL_NSIREQUESTOBSERVER
60 explicit ImageListener(ImageDocument* aDocument);
61 virtual ~ImageListener();
64 ImageListener::ImageListener(ImageDocument* aDocument)
65 : MediaDocumentStreamListener(aDocument)
69 ImageListener::~ImageListener()
73 NS_IMETHODIMP
74 ImageListener::OnStartRequest(nsIRequest* request, nsISupports *ctxt)
76 NS_ENSURE_TRUE(mDocument, NS_ERROR_FAILURE);
78 ImageDocument *imgDoc = static_cast<ImageDocument*>(mDocument.get());
79 nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
80 if (!channel) {
81 return NS_ERROR_FAILURE;
84 nsCOMPtr<nsPIDOMWindow> domWindow = imgDoc->GetWindow();
85 NS_ENSURE_TRUE(domWindow, NS_ERROR_UNEXPECTED);
87 // Do a ShouldProcess check to see whether to keep loading the image.
88 nsCOMPtr<nsIURI> channelURI;
89 channel->GetURI(getter_AddRefs(channelURI));
91 nsAutoCString mimeType;
92 channel->GetContentType(mimeType);
94 nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
95 nsCOMPtr<nsIPrincipal> channelPrincipal;
96 if (secMan) {
97 secMan->GetChannelResultPrincipal(channel, getter_AddRefs(channelPrincipal));
100 int16_t decision = nsIContentPolicy::ACCEPT;
101 nsresult rv = NS_CheckContentProcessPolicy(nsIContentPolicy::TYPE_IMAGE,
102 channelURI,
103 channelPrincipal,
104 domWindow->GetFrameElementInternal(),
105 mimeType,
106 nullptr,
107 &decision,
108 nsContentUtils::GetContentPolicy(),
109 secMan);
111 if (NS_FAILED(rv) || NS_CP_REJECTED(decision)) {
112 request->Cancel(NS_ERROR_CONTENT_BLOCKED);
113 return NS_OK;
116 if (!imgDoc->mObservingImageLoader) {
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));
125 return MediaDocumentStreamListener::OnStartRequest(request, ctxt);
128 NS_IMETHODIMP
129 ImageListener::OnStopRequest(nsIRequest* aRequest, nsISupports* aCtxt, nsresult aStatus)
131 ImageDocument* imgDoc = static_cast<ImageDocument*>(mDocument.get());
132 nsContentUtils::DispatchChromeEvent(imgDoc, static_cast<nsIDocument*>(imgDoc),
133 NS_LITERAL_STRING("ImageContentLoaded"),
134 true, true);
135 return MediaDocumentStreamListener::OnStopRequest(aRequest, aCtxt, aStatus);
138 ImageDocument::ImageDocument()
139 : MediaDocument(),
140 mOriginalZoomLevel(1.0)
142 // NOTE! nsDocument::operator new() zeroes out all members, so don't
143 // bother initializing members to 0.
146 ImageDocument::~ImageDocument()
151 NS_IMPL_CYCLE_COLLECTION_INHERITED(ImageDocument, MediaDocument,
152 mImageContent)
154 NS_IMPL_ADDREF_INHERITED(ImageDocument, MediaDocument)
155 NS_IMPL_RELEASE_INHERITED(ImageDocument, MediaDocument)
157 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(ImageDocument)
158 NS_INTERFACE_TABLE_INHERITED(ImageDocument, nsIImageDocument,
159 imgINotificationObserver, nsIDOMEventListener)
160 NS_INTERFACE_TABLE_TAIL_INHERITING(MediaDocument)
163 nsresult
164 ImageDocument::Init()
166 nsresult rv = MediaDocument::Init();
167 NS_ENSURE_SUCCESS(rv, rv);
169 mResizeImageByDefault = Preferences::GetBool(AUTOMATIC_IMAGE_RESIZING_PREF);
170 mClickResizingEnabled = Preferences::GetBool(CLICK_IMAGE_RESIZING_PREF);
171 mShouldResize = mResizeImageByDefault;
172 mFirstResize = true;
174 return NS_OK;
177 JSObject*
178 ImageDocument::WrapNode(JSContext* aCx)
180 return ImageDocumentBinding::Wrap(aCx, this);
183 nsresult
184 ImageDocument::StartDocumentLoad(const char* aCommand,
185 nsIChannel* aChannel,
186 nsILoadGroup* aLoadGroup,
187 nsISupports* aContainer,
188 nsIStreamListener** aDocListener,
189 bool aReset,
190 nsIContentSink* aSink)
192 nsresult rv =
193 MediaDocument::StartDocumentLoad(aCommand, aChannel, aLoadGroup, aContainer,
194 aDocListener, aReset, aSink);
195 if (NS_FAILED(rv)) {
196 return rv;
199 mOriginalZoomLevel =
200 Preferences::GetBool(SITE_SPECIFIC_ZOOM, false) ? 1.0 : GetZoomLevel();
202 NS_ASSERTION(aDocListener, "null aDocListener");
203 *aDocListener = new ImageListener(this);
204 NS_ADDREF(*aDocListener);
206 return NS_OK;
209 void
210 ImageDocument::Destroy()
212 if (mImageContent) {
213 // Remove our event listener from the image content.
214 nsCOMPtr<EventTarget> target = do_QueryInterface(mImageContent);
215 target->RemoveEventListener(NS_LITERAL_STRING("load"), this, false);
216 target->RemoveEventListener(NS_LITERAL_STRING("click"), this, false);
218 // Break reference cycle with mImageContent, if we have one
219 if (mObservingImageLoader) {
220 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mImageContent);
221 if (imageLoader) {
222 imageLoader->RemoveObserver(this);
226 mImageContent = nullptr;
229 MediaDocument::Destroy();
232 void
233 ImageDocument::SetScriptGlobalObject(nsIScriptGlobalObject* aScriptGlobalObject)
235 // If the script global object is changing, we need to unhook our event
236 // listeners on the window.
237 nsCOMPtr<EventTarget> target;
238 if (mScriptGlobalObject &&
239 aScriptGlobalObject != mScriptGlobalObject) {
240 target = do_QueryInterface(mScriptGlobalObject);
241 target->RemoveEventListener(NS_LITERAL_STRING("resize"), this, false);
242 target->RemoveEventListener(NS_LITERAL_STRING("keypress"), this,
243 false);
246 // Set the script global object on the superclass before doing
247 // anything that might require it....
248 MediaDocument::SetScriptGlobalObject(aScriptGlobalObject);
250 if (aScriptGlobalObject) {
251 if (!GetRootElement()) {
252 // Create synthetic document
253 #ifdef DEBUG
254 nsresult rv =
255 #endif
256 CreateSyntheticDocument();
257 NS_ASSERTION(NS_SUCCEEDED(rv), "failed to create synthetic document");
259 target = do_QueryInterface(mImageContent);
260 target->AddEventListener(NS_LITERAL_STRING("load"), this, false);
261 target->AddEventListener(NS_LITERAL_STRING("click"), this, false);
264 target = do_QueryInterface(aScriptGlobalObject);
265 target->AddEventListener(NS_LITERAL_STRING("resize"), this, false);
266 target->AddEventListener(NS_LITERAL_STRING("keypress"), this, false);
268 if (GetReadyStateEnum() != nsIDocument::READYSTATE_COMPLETE) {
269 LinkStylesheet(NS_LITERAL_STRING("resource://gre/res/ImageDocument.css"));
270 if (!nsContentUtils::IsChildOfSameType(this)) {
271 LinkStylesheet(NS_LITERAL_STRING("resource://gre/res/TopLevelImageDocument.css"));
272 LinkStylesheet(NS_LITERAL_STRING("chrome://global/skin/media/TopLevelImageDocument.css"));
275 BecomeInteractive();
279 void
280 ImageDocument::OnPageShow(bool aPersisted,
281 EventTarget* aDispatchStartTarget)
283 if (aPersisted) {
284 mOriginalZoomLevel =
285 Preferences::GetBool(SITE_SPECIFIC_ZOOM, false) ? 1.0 : GetZoomLevel();
287 MediaDocument::OnPageShow(aPersisted, aDispatchStartTarget);
290 NS_IMETHODIMP
291 ImageDocument::GetImageResizingEnabled(bool* aImageResizingEnabled)
293 *aImageResizingEnabled = ImageResizingEnabled();
294 return NS_OK;
297 NS_IMETHODIMP
298 ImageDocument::GetImageIsOverflowing(bool* aImageIsOverflowing)
300 *aImageIsOverflowing = ImageIsOverflowing();
301 return NS_OK;
304 NS_IMETHODIMP
305 ImageDocument::GetImageIsResized(bool* aImageIsResized)
307 *aImageIsResized = ImageIsResized();
308 return NS_OK;
311 already_AddRefed<imgIRequest>
312 ImageDocument::GetImageRequest(ErrorResult& aRv)
314 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mImageContent);
315 nsCOMPtr<imgIRequest> imageRequest;
316 if (imageLoader) {
317 aRv = imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
318 getter_AddRefs(imageRequest));
320 return imageRequest.forget();
323 NS_IMETHODIMP
324 ImageDocument::GetImageRequest(imgIRequest** aImageRequest)
326 ErrorResult rv;
327 *aImageRequest = GetImageRequest(rv).take();
328 return rv.ErrorCode();
331 void
332 ImageDocument::ShrinkToFit()
334 if (!mImageContent) {
335 return;
337 if (GetZoomLevel() != mOriginalZoomLevel && mImageIsResized &&
338 !nsContentUtils::IsChildOfSameType(this)) {
339 return;
342 // Keep image content alive while changing the attributes.
343 nsCOMPtr<nsIContent> imageContent = mImageContent;
344 nsCOMPtr<nsIDOMHTMLImageElement> image = do_QueryInterface(mImageContent);
345 image->SetWidth(std::max(1, NSToCoordFloor(GetRatio() * mImageWidth)));
346 image->SetHeight(std::max(1, NSToCoordFloor(GetRatio() * mImageHeight)));
348 // The view might have been scrolled when zooming in, scroll back to the
349 // origin now that we're showing a shrunk-to-window version.
350 ScrollImageTo(0, 0, false);
352 if (!mImageContent) {
353 // ScrollImageTo flush destroyed our content.
354 return;
357 SetModeClass(eShrinkToFit);
359 mImageIsResized = true;
361 UpdateTitleAndCharset();
364 NS_IMETHODIMP
365 ImageDocument::DOMShrinkToFit()
367 ShrinkToFit();
368 return NS_OK;
371 NS_IMETHODIMP
372 ImageDocument::DOMRestoreImageTo(int32_t aX, int32_t aY)
374 RestoreImageTo(aX, aY);
375 return NS_OK;
378 void
379 ImageDocument::ScrollImageTo(int32_t aX, int32_t aY, bool restoreImage)
381 float ratio = GetRatio();
383 if (restoreImage) {
384 RestoreImage();
385 FlushPendingNotifications(Flush_Layout);
388 nsCOMPtr<nsIPresShell> shell = GetShell();
389 if (!shell) {
390 return;
393 nsIScrollableFrame* sf = shell->GetRootScrollFrameAsScrollable();
394 if (!sf) {
395 return;
398 nsRect portRect = sf->GetScrollPortRect();
399 sf->ScrollTo(nsPoint(nsPresContext::CSSPixelsToAppUnits(aX/ratio) - portRect.width/2,
400 nsPresContext::CSSPixelsToAppUnits(aY/ratio) - portRect.height/2),
401 nsIScrollableFrame::INSTANT);
404 void
405 ImageDocument::RestoreImage()
407 if (!mImageContent) {
408 return;
410 // Keep image content alive while changing the attributes.
411 nsCOMPtr<nsIContent> imageContent = mImageContent;
412 imageContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::width, true);
413 imageContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::height, true);
415 if (mImageIsOverflowing) {
416 SetModeClass(eOverflowing);
418 else {
419 SetModeClass(eNone);
422 mImageIsResized = false;
424 UpdateTitleAndCharset();
427 NS_IMETHODIMP
428 ImageDocument::DOMRestoreImage()
430 RestoreImage();
431 return NS_OK;
434 void
435 ImageDocument::ToggleImageSize()
437 mShouldResize = true;
438 if (mImageIsResized) {
439 mShouldResize = false;
440 ResetZoomLevel();
441 RestoreImage();
443 else if (mImageIsOverflowing) {
444 ResetZoomLevel();
445 ShrinkToFit();
449 NS_IMETHODIMP
450 ImageDocument::DOMToggleImageSize()
452 ToggleImageSize();
453 return NS_OK;
456 NS_IMETHODIMP
457 ImageDocument::Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aData)
459 if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
460 nsCOMPtr<imgIContainer> image;
461 aRequest->GetImage(getter_AddRefs(image));
462 return OnSizeAvailable(aRequest, image);
465 // Run this using a script runner because HAS_TRANSPARENCY notifications can
466 // come during painting and this will trigger invalidation.
467 if (aType == imgINotificationObserver::HAS_TRANSPARENCY) {
468 nsCOMPtr<nsIRunnable> runnable =
469 NS_NewRunnableMethod(this, &ImageDocument::OnHasTransparency);
470 nsContentUtils::AddScriptRunner(runnable);
473 if (aType == imgINotificationObserver::LOAD_COMPLETE) {
474 uint32_t reqStatus;
475 aRequest->GetImageStatus(&reqStatus);
476 nsresult status =
477 reqStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK;
478 return OnLoadComplete(aRequest, status);
481 return NS_OK;
484 void
485 ImageDocument::OnHasTransparency()
487 if (!mImageContent || nsContentUtils::IsChildOfSameType(this)) {
488 return;
491 nsDOMTokenList* classList = mImageContent->AsElement()->ClassList();
492 mozilla::ErrorResult rv;
493 classList->Add(NS_LITERAL_STRING("transparent"), rv);
496 void
497 ImageDocument::SetModeClass(eModeClasses mode)
499 nsDOMTokenList* classList = mImageContent->AsElement()->ClassList();
500 mozilla::ErrorResult rv;
502 if (mode == eShrinkToFit) {
503 classList->Add(NS_LITERAL_STRING("shrinkToFit"), rv);
504 } else {
505 classList->Remove(NS_LITERAL_STRING("shrinkToFit"), rv);
508 if (mode == eOverflowing) {
509 classList->Add(NS_LITERAL_STRING("overflowing"), rv);
510 } else {
511 classList->Remove(NS_LITERAL_STRING("overflowing"), rv);
515 nsresult
516 ImageDocument::OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage)
518 // Styles have not yet been applied, so we don't know the final size. For now,
519 // default to the image's intrinsic size.
520 aImage->GetWidth(&mImageWidth);
521 aImage->GetHeight(&mImageHeight);
523 nsCOMPtr<nsIRunnable> runnable =
524 NS_NewRunnableMethod(this, &ImageDocument::DefaultCheckOverflowing);
525 nsContentUtils::AddScriptRunner(runnable);
526 UpdateTitleAndCharset();
528 return NS_OK;
531 nsresult
532 ImageDocument::OnLoadComplete(imgIRequest* aRequest, nsresult aStatus)
534 UpdateTitleAndCharset();
536 // mImageContent can be null if the document is already destroyed
537 if (NS_FAILED(aStatus) && mStringBundle && mImageContent) {
538 nsAutoCString src;
539 mDocumentURI->GetSpec(src);
540 NS_ConvertUTF8toUTF16 srcString(src);
541 const char16_t* formatString[] = { srcString.get() };
542 nsXPIDLString errorMsg;
543 NS_NAMED_LITERAL_STRING(str, "InvalidImage");
544 mStringBundle->FormatStringFromName(str.get(), formatString, 1,
545 getter_Copies(errorMsg));
547 mImageContent->SetAttr(kNameSpaceID_None, nsGkAtoms::alt, errorMsg, false);
550 return NS_OK;
553 NS_IMETHODIMP
554 ImageDocument::HandleEvent(nsIDOMEvent* aEvent)
556 nsAutoString eventType;
557 aEvent->GetType(eventType);
558 if (eventType.EqualsLiteral("resize")) {
559 CheckOverflowing(false);
561 else if (eventType.EqualsLiteral("click") && mClickResizingEnabled) {
562 ResetZoomLevel();
563 mShouldResize = true;
564 if (mImageIsResized) {
565 int32_t x = 0, y = 0;
566 nsCOMPtr<nsIDOMMouseEvent> event(do_QueryInterface(aEvent));
567 if (event) {
568 event->GetClientX(&x);
569 event->GetClientY(&y);
570 int32_t left = 0, top = 0;
571 nsCOMPtr<nsIDOMHTMLElement> htmlElement =
572 do_QueryInterface(mImageContent);
573 htmlElement->GetOffsetLeft(&left);
574 htmlElement->GetOffsetTop(&top);
575 x -= left;
576 y -= top;
578 mShouldResize = false;
579 RestoreImageTo(x, y);
581 else if (mImageIsOverflowing) {
582 ShrinkToFit();
584 } else if (eventType.EqualsLiteral("load")) {
585 UpdateSizeFromLayout();
588 return NS_OK;
591 void
592 ImageDocument::UpdateSizeFromLayout()
594 // Pull an updated size from the content frame to account for any size
595 // change due to CSS properties like |image-orientation|.
596 Element* contentElement = mImageContent->AsElement();
597 if (!contentElement) {
598 return;
601 nsIFrame* contentFrame = contentElement->GetPrimaryFrame(Flush_Frames);
602 if (!contentFrame) {
603 return;
606 nsIntSize oldSize(mImageWidth, mImageHeight);
607 IntrinsicSize newSize = contentFrame->GetIntrinsicSize();
609 if (newSize.width.GetUnit() == eStyleUnit_Coord) {
610 mImageWidth = nsPresContext::AppUnitsToFloatCSSPixels(newSize.width.GetCoordValue());
612 if (newSize.height.GetUnit() == eStyleUnit_Coord) {
613 mImageHeight = nsPresContext::AppUnitsToFloatCSSPixels(newSize.height.GetCoordValue());
616 // Ensure that our information about overflow is up-to-date if needed.
617 if (mImageWidth != oldSize.width || mImageHeight != oldSize.height) {
618 CheckOverflowing(false);
622 nsresult
623 ImageDocument::CreateSyntheticDocument()
625 // Synthesize an html document that refers to the image
626 nsresult rv = MediaDocument::CreateSyntheticDocument();
627 NS_ENSURE_SUCCESS(rv, rv);
629 // Add the image element
630 Element* body = GetBodyElement();
631 if (!body) {
632 NS_WARNING("no body on image document!");
633 return NS_ERROR_FAILURE;
636 nsRefPtr<NodeInfo> nodeInfo;
637 nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::img, nullptr,
638 kNameSpaceID_XHTML,
639 nsIDOMNode::ELEMENT_NODE);
641 mImageContent = NS_NewHTMLImageElement(nodeInfo.forget());
642 if (!mImageContent) {
643 return NS_ERROR_OUT_OF_MEMORY;
645 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mImageContent);
646 NS_ENSURE_TRUE(imageLoader, NS_ERROR_UNEXPECTED);
648 nsAutoCString src;
649 mDocumentURI->GetSpec(src);
651 NS_ConvertUTF8toUTF16 srcString(src);
652 // Make sure not to start the image load from here...
653 imageLoader->SetLoadingEnabled(false);
654 mImageContent->SetAttr(kNameSpaceID_None, nsGkAtoms::src, srcString, false);
655 mImageContent->SetAttr(kNameSpaceID_None, nsGkAtoms::alt, srcString, false);
657 body->AppendChildTo(mImageContent, false);
658 imageLoader->SetLoadingEnabled(true);
660 return NS_OK;
663 nsresult
664 ImageDocument::CheckOverflowing(bool changeState)
666 /* Create a scope so that the style context gets destroyed before we might
667 * call RebuildStyleData. Also, holding onto pointers to the
668 * presentation through style resolution is potentially dangerous.
671 nsIPresShell *shell = GetShell();
672 if (!shell) {
673 return NS_OK;
676 nsPresContext *context = shell->GetPresContext();
677 nsRect visibleArea = context->GetVisibleArea();
679 mVisibleWidth = nsPresContext::AppUnitsToFloatCSSPixels(visibleArea.width);
680 mVisibleHeight = nsPresContext::AppUnitsToFloatCSSPixels(visibleArea.height);
683 bool imageWasOverflowing = mImageIsOverflowing;
684 mImageIsOverflowing =
685 mImageWidth > mVisibleWidth || mImageHeight > mVisibleHeight;
686 bool windowBecameBigEnough = imageWasOverflowing && !mImageIsOverflowing;
688 if (changeState || mShouldResize || mFirstResize ||
689 windowBecameBigEnough) {
690 if (mImageIsOverflowing && (changeState || mShouldResize)) {
691 ShrinkToFit();
693 else if (mImageIsResized || mFirstResize || windowBecameBigEnough) {
694 RestoreImage();
697 mFirstResize = false;
699 return NS_OK;
702 void
703 ImageDocument::UpdateTitleAndCharset()
705 nsAutoCString typeStr;
706 nsCOMPtr<imgIRequest> imageRequest;
707 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mImageContent);
708 if (imageLoader) {
709 imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
710 getter_AddRefs(imageRequest));
713 if (imageRequest) {
714 nsXPIDLCString mimeType;
715 imageRequest->GetMimeType(getter_Copies(mimeType));
716 ToUpperCase(mimeType);
717 nsXPIDLCString::const_iterator start, end;
718 mimeType.BeginReading(start);
719 mimeType.EndReading(end);
720 nsXPIDLCString::const_iterator iter = end;
721 if (FindInReadable(NS_LITERAL_CSTRING("IMAGE/"), start, iter) &&
722 iter != end) {
723 // strip out "X-" if any
724 if (*iter == 'X') {
725 ++iter;
726 if (iter != end && *iter == '-') {
727 ++iter;
728 if (iter == end) {
729 // looks like "IMAGE/X-" is the type?? Bail out of here.
730 mimeType.BeginReading(iter);
732 } else {
733 --iter;
736 typeStr = Substring(iter, end);
737 } else {
738 typeStr = mimeType;
742 nsXPIDLString status;
743 if (mImageIsResized) {
744 nsAutoString ratioStr;
745 ratioStr.AppendInt(NSToCoordFloor(GetRatio() * 100));
747 const char16_t* formatString[1] = { ratioStr.get() };
748 mStringBundle->FormatStringFromName(MOZ_UTF16("ScaledImage"),
749 formatString, 1,
750 getter_Copies(status));
753 static const char* const formatNames[4] =
755 "ImageTitleWithNeitherDimensionsNorFile",
756 "ImageTitleWithoutDimensions",
757 "ImageTitleWithDimensions2",
758 "ImageTitleWithDimensions2AndFile",
761 MediaDocument::UpdateTitleAndCharset(typeStr, mChannel, formatNames,
762 mImageWidth, mImageHeight, status);
765 void
766 ImageDocument::ResetZoomLevel()
768 nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
769 if (docShell) {
770 if (nsContentUtils::IsChildOfSameType(this)) {
771 return;
774 nsCOMPtr<nsIContentViewer> cv;
775 docShell->GetContentViewer(getter_AddRefs(cv));
776 if (cv) {
777 cv->SetFullZoom(mOriginalZoomLevel);
782 float
783 ImageDocument::GetZoomLevel()
785 float zoomLevel = mOriginalZoomLevel;
786 nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
787 if (docShell) {
788 nsCOMPtr<nsIContentViewer> cv;
789 docShell->GetContentViewer(getter_AddRefs(cv));
790 if (cv) {
791 cv->GetFullZoom(&zoomLevel);
794 return zoomLevel;
797 } // namespace dom
798 } // namespace mozilla
800 nsresult
801 NS_NewImageDocument(nsIDocument** aResult)
803 mozilla::dom::ImageDocument* doc = new mozilla::dom::ImageDocument();
804 NS_ADDREF(doc);
806 nsresult rv = doc->Init();
807 if (NS_FAILED(rv)) {
808 NS_RELEASE(doc);
811 *aResult = doc;
813 return rv;