/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "MediaDocument.h" #include "nsRect.h" #include "nsHTMLDocument.h" #include "nsIImageDocument.h" #include "nsIImageLoadingContent.h" #include "nsGenericHTMLElement.h" #include "nsIDOMHTMLImageElement.h" #include "nsIDOMEvent.h" #include "nsIDOMKeyEvent.h" #include "nsIDOMMouseEvent.h" #include "nsIDOMEventListener.h" #include "nsGkAtoms.h" #include "imgIRequest.h" #include "imgILoader.h" #include "imgIContainer.h" #include "nsStubImageDecoderObserver.h" #include "nsIPresShell.h" #include "nsPresContext.h" #include "nsStyleContext.h" #include "nsAutoPtr.h" #include "nsStyleSet.h" #include "nsIChannel.h" #include "nsIContentPolicy.h" #include "nsContentPolicyUtils.h" #include "nsPIDOMWindow.h" #include "nsIDOMElement.h" #include "nsIDOMHTMLElement.h" #include "nsError.h" #include "nsURILoader.h" #include "nsIDocShell.h" #include "nsIContentViewer.h" #include "nsIMarkupDocumentViewer.h" #include "nsIDocShellTreeItem.h" #include "nsThreadUtils.h" #include "nsIScrollableFrame.h" #include "nsContentUtils.h" #include "mozilla/dom/Element.h" #include "mozilla/Preferences.h" #define AUTOMATIC_IMAGE_RESIZING_PREF "browser.enable_automatic_image_resizing" #define CLICK_IMAGE_RESIZING_PREF "browser.enable_click_image_resizing" //XXX A hack needed for Firefox's site specific zoom. #define SITE_SPECIFIC_ZOOM "browser.zoom.siteSpecific" namespace mozilla { namespace dom { class ImageDocument; class ImageListener : public MediaDocumentStreamListener { public: ImageListener(ImageDocument* aDocument); virtual ~ImageListener(); /* nsIRequestObserver */ NS_IMETHOD OnStartRequest(nsIRequest* request, nsISupports *ctxt); }; class ImageDocument : public MediaDocument , public nsIImageDocument , public nsStubImageDecoderObserver , public nsIDOMEventListener { public: ImageDocument(); virtual ~ImageDocument(); NS_DECL_ISUPPORTS_INHERITED virtual nsresult Init(); virtual nsresult StartDocumentLoad(const char* aCommand, nsIChannel* aChannel, nsILoadGroup* aLoadGroup, nsISupports* aContainer, nsIStreamListener** aDocListener, bool aReset = true, nsIContentSink* aSink = nullptr); virtual void SetScriptGlobalObject(nsIScriptGlobalObject* aScriptGlobalObject); virtual void Destroy(); virtual void OnPageShow(bool aPersisted, nsIDOMEventTarget* aDispatchStartTarget); NS_DECL_NSIIMAGEDOCUMENT // imgIDecoderObserver (override nsStubImageDecoderObserver) NS_IMETHOD OnStartContainer(imgIRequest* aRequest, imgIContainer* aImage); NS_IMETHOD OnStopContainer(imgIRequest* aRequest, imgIContainer* aImage); NS_IMETHOD OnStopDecode(imgIRequest *aRequest, nsresult aStatus, const PRUnichar *aStatusArg); NS_IMETHOD OnDiscard(imgIRequest *aRequest); // nsIDOMEventListener NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent); NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ImageDocument, MediaDocument) friend class ImageListener; void DefaultCheckOverflowing() { CheckOverflowing(mResizeImageByDefault); } virtual nsXPCClassInfo* GetClassInfo(); protected: virtual nsresult CreateSyntheticDocument(); nsresult CheckOverflowing(bool changeState); void UpdateTitleAndCharset(); nsresult ScrollImageTo(PRInt32 aX, PRInt32 aY, bool restoreImage); float GetRatio() { return NS_MIN((float)mVisibleWidth / mImageWidth, (float)mVisibleHeight / mImageHeight); } void ResetZoomLevel(); float GetZoomLevel(); nsCOMPtr mImageContent; PRInt32 mVisibleWidth; PRInt32 mVisibleHeight; PRInt32 mImageWidth; PRInt32 mImageHeight; bool mResizeImageByDefault; bool mClickResizingEnabled; bool mImageIsOverflowing; // mImageIsResized is true if the image is currently resized bool mImageIsResized; // mShouldResize is true if the image should be resized when it doesn't fit // mImageIsResized cannot be true when this is false, but mImageIsResized // can be false when this is true bool mShouldResize; bool mFirstResize; // mObservingImageLoader is true while the observer is set. bool mObservingImageLoader; float mOriginalZoomLevel; }; ImageListener::ImageListener(ImageDocument* aDocument) : MediaDocumentStreamListener(aDocument) { } ImageListener::~ImageListener() { } NS_IMETHODIMP ImageListener::OnStartRequest(nsIRequest* request, nsISupports *ctxt) { NS_ENSURE_TRUE(mDocument, NS_ERROR_FAILURE); ImageDocument *imgDoc = static_cast(mDocument.get()); nsCOMPtr channel = do_QueryInterface(request); if (!channel) { return NS_ERROR_FAILURE; } nsCOMPtr domWindow = do_QueryInterface(imgDoc->GetScriptGlobalObject()); NS_ENSURE_TRUE(domWindow, NS_ERROR_UNEXPECTED); // Do a ShouldProcess check to see whether to keep loading the image. nsCOMPtr channelURI; channel->GetURI(getter_AddRefs(channelURI)); nsCAutoString mimeType; channel->GetContentType(mimeType); nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); nsCOMPtr channelPrincipal; if (secMan) { secMan->GetChannelPrincipal(channel, getter_AddRefs(channelPrincipal)); } PRInt16 decision = nsIContentPolicy::ACCEPT; nsresult rv = NS_CheckContentProcessPolicy(nsIContentPolicy::TYPE_IMAGE, channelURI, channelPrincipal, domWindow->GetFrameElementInternal(), mimeType, nullptr, &decision, nsContentUtils::GetContentPolicy(), secMan); if (NS_FAILED(rv) || NS_CP_REJECTED(decision)) { request->Cancel(NS_ERROR_CONTENT_BLOCKED); return NS_OK; } nsCOMPtr imageLoader = do_QueryInterface(imgDoc->mImageContent); NS_ENSURE_TRUE(imageLoader, NS_ERROR_UNEXPECTED); imageLoader->AddObserver(imgDoc); imgDoc->mObservingImageLoader = true; imageLoader->LoadImageWithChannel(channel, getter_AddRefs(mNextStream)); return MediaDocumentStreamListener::OnStartRequest(request, ctxt); } ImageDocument::ImageDocument() : mOriginalZoomLevel(1.0) { // NOTE! nsDocument::operator new() zeroes out all members, so don't // bother initializing members to 0. } ImageDocument::~ImageDocument() { } NS_IMPL_CYCLE_COLLECTION_CLASS(ImageDocument) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ImageDocument, MediaDocument) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mImageContent) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ImageDocument, MediaDocument) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mImageContent) NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_ADDREF_INHERITED(ImageDocument, MediaDocument) NS_IMPL_RELEASE_INHERITED(ImageDocument, MediaDocument) DOMCI_NODE_DATA(ImageDocument, ImageDocument) NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(ImageDocument) NS_HTML_DOCUMENT_INTERFACE_TABLE_BEGIN(ImageDocument) NS_INTERFACE_TABLE_ENTRY(ImageDocument, nsIImageDocument) NS_INTERFACE_TABLE_ENTRY(ImageDocument, imgIDecoderObserver) NS_INTERFACE_TABLE_ENTRY(ImageDocument, imgIContainerObserver) NS_INTERFACE_TABLE_ENTRY(ImageDocument, nsIDOMEventListener) NS_OFFSET_AND_INTERFACE_TABLE_END NS_OFFSET_AND_INTERFACE_TABLE_TO_MAP_SEGUE NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(ImageDocument) NS_INTERFACE_MAP_END_INHERITING(MediaDocument) nsresult ImageDocument::Init() { nsresult rv = MediaDocument::Init(); NS_ENSURE_SUCCESS(rv, rv); mResizeImageByDefault = Preferences::GetBool(AUTOMATIC_IMAGE_RESIZING_PREF); mClickResizingEnabled = Preferences::GetBool(CLICK_IMAGE_RESIZING_PREF); mShouldResize = mResizeImageByDefault; mFirstResize = true; return NS_OK; } nsresult ImageDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel, nsILoadGroup* aLoadGroup, nsISupports* aContainer, nsIStreamListener** aDocListener, bool aReset, nsIContentSink* aSink) { nsresult rv = MediaDocument::StartDocumentLoad(aCommand, aChannel, aLoadGroup, aContainer, aDocListener, aReset, aSink); if (NS_FAILED(rv)) { return rv; } mOriginalZoomLevel = Preferences::GetBool(SITE_SPECIFIC_ZOOM, false) ? 1.0 : GetZoomLevel(); NS_ASSERTION(aDocListener, "null aDocListener"); *aDocListener = new ImageListener(this); NS_ADDREF(*aDocListener); return NS_OK; } void ImageDocument::Destroy() { if (mImageContent) { // Remove our event listener from the image content. nsCOMPtr target = do_QueryInterface(mImageContent); target->RemoveEventListener(NS_LITERAL_STRING("click"), this, false); // Break reference cycle with mImageContent, if we have one if (mObservingImageLoader) { nsCOMPtr imageLoader = do_QueryInterface(mImageContent); if (imageLoader) { // Push a null JSContext on the stack so that code that // nsImageLoadingContent doesn't think it's being called by JS. See // Bug 631241 nsCxPusher pusher; pusher.PushNull(); imageLoader->RemoveObserver(this); } } mImageContent = nullptr; } MediaDocument::Destroy(); } void ImageDocument::SetScriptGlobalObject(nsIScriptGlobalObject* aScriptGlobalObject) { // If the script global object is changing, we need to unhook our event // listeners on the window. nsCOMPtr target; if (mScriptGlobalObject && aScriptGlobalObject != mScriptGlobalObject) { target = do_QueryInterface(mScriptGlobalObject); target->RemoveEventListener(NS_LITERAL_STRING("resize"), this, false); target->RemoveEventListener(NS_LITERAL_STRING("keypress"), this, false); } // Set the script global object on the superclass before doing // anything that might require it.... MediaDocument::SetScriptGlobalObject(aScriptGlobalObject); if (aScriptGlobalObject) { if (!GetRootElement()) { // Create synthetic document #ifdef DEBUG nsresult rv = #endif CreateSyntheticDocument(); NS_ASSERTION(NS_SUCCEEDED(rv), "failed to create synthetic document"); target = do_QueryInterface(mImageContent); target->AddEventListener(NS_LITERAL_STRING("click"), this, false); } target = do_QueryInterface(aScriptGlobalObject); target->AddEventListener(NS_LITERAL_STRING("resize"), this, false); target->AddEventListener(NS_LITERAL_STRING("keypress"), this, false); if (!nsContentUtils::IsChildOfSameType(this) && GetReadyStateEnum() != nsIDocument::READYSTATE_COMPLETE) { LinkStylesheet(NS_LITERAL_STRING("resource://gre/res/TopLevelImageDocument.css")); LinkStylesheet(NS_LITERAL_STRING("chrome://global/skin/TopLevelImageDocument.css")); } BecomeInteractive(); } } void ImageDocument::OnPageShow(bool aPersisted, nsIDOMEventTarget* aDispatchStartTarget) { if (aPersisted) { mOriginalZoomLevel = Preferences::GetBool(SITE_SPECIFIC_ZOOM, false) ? 1.0 : GetZoomLevel(); } MediaDocument::OnPageShow(aPersisted, aDispatchStartTarget); } NS_IMETHODIMP ImageDocument::GetImageResizingEnabled(bool* aImageResizingEnabled) { *aImageResizingEnabled = true; return NS_OK; } NS_IMETHODIMP ImageDocument::GetImageIsOverflowing(bool* aImageIsOverflowing) { *aImageIsOverflowing = mImageIsOverflowing; return NS_OK; } NS_IMETHODIMP ImageDocument::GetImageIsResized(bool* aImageIsResized) { *aImageIsResized = mImageIsResized; return NS_OK; } NS_IMETHODIMP ImageDocument::GetImageRequest(imgIRequest** aImageRequest) { nsCOMPtr imageLoader = do_QueryInterface(mImageContent); if (imageLoader) { return imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, aImageRequest); } *aImageRequest = nullptr; return NS_OK; } NS_IMETHODIMP ImageDocument::ShrinkToFit() { if (!mImageContent) { return NS_OK; } if (GetZoomLevel() != mOriginalZoomLevel && mImageIsResized && !nsContentUtils::IsChildOfSameType(this)) { return NS_OK; } // Keep image content alive while changing the attributes. nsCOMPtr imageContent = mImageContent; nsCOMPtr image = do_QueryInterface(mImageContent); image->SetWidth(NS_MAX(1, NSToCoordFloor(GetRatio() * mImageWidth))); image->SetHeight(NS_MAX(1, NSToCoordFloor(GetRatio() * mImageHeight))); // The view might have been scrolled when zooming in, scroll back to the // origin now that we're showing a shrunk-to-window version. (void) ScrollImageTo(0, 0, false); imageContent->SetAttr(kNameSpaceID_None, nsGkAtoms::style, NS_LITERAL_STRING("cursor: -moz-zoom-in"), true); mImageIsResized = true; UpdateTitleAndCharset(); return NS_OK; } NS_IMETHODIMP ImageDocument::RestoreImageTo(PRInt32 aX, PRInt32 aY) { return ScrollImageTo(aX, aY, true); } nsresult ImageDocument::ScrollImageTo(PRInt32 aX, PRInt32 aY, bool restoreImage) { float ratio = GetRatio(); if (restoreImage) { RestoreImage(); FlushPendingNotifications(Flush_Layout); } nsIPresShell *shell = GetShell(); if (!shell) return NS_OK; nsIScrollableFrame* sf = shell->GetRootScrollFrameAsScrollable(); if (!sf) return NS_OK; nsRect portRect = sf->GetScrollPortRect(); sf->ScrollTo(nsPoint(nsPresContext::CSSPixelsToAppUnits(aX/ratio) - portRect.width/2, nsPresContext::CSSPixelsToAppUnits(aY/ratio) - portRect.height/2), nsIScrollableFrame::INSTANT); return NS_OK; } NS_IMETHODIMP ImageDocument::RestoreImage() { if (!mImageContent) { return NS_OK; } // Keep image content alive while changing the attributes. nsCOMPtr imageContent = mImageContent; imageContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::width, true); imageContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::height, true); if (mImageIsOverflowing) { imageContent->SetAttr(kNameSpaceID_None, nsGkAtoms::style, NS_LITERAL_STRING("cursor: -moz-zoom-out"), true); } else { imageContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::style, true); } mImageIsResized = false; UpdateTitleAndCharset(); return NS_OK; } NS_IMETHODIMP ImageDocument::ToggleImageSize() { mShouldResize = true; if (mImageIsResized) { mShouldResize = false; ResetZoomLevel(); RestoreImage(); } else if (mImageIsOverflowing) { ResetZoomLevel(); ShrinkToFit(); } return NS_OK; } NS_IMETHODIMP ImageDocument::OnStartContainer(imgIRequest* aRequest, imgIContainer* aImage) { aImage->GetWidth(&mImageWidth); aImage->GetHeight(&mImageHeight); nsCOMPtr runnable = NS_NewRunnableMethod(this, &ImageDocument::DefaultCheckOverflowing); nsContentUtils::AddScriptRunner(runnable); UpdateTitleAndCharset(); return NS_OK; } NS_IMETHODIMP ImageDocument::OnStopContainer(imgIRequest* aRequest, imgIContainer* aImage) { if (mImageContent) { // Update the background-color of the image only after the // image has been decoded to prevent flashes of just the // background-color. mImageContent->SetAttr(kNameSpaceID_None, nsGkAtoms::_class, NS_LITERAL_STRING("decoded"), true); } return NS_OK; } NS_IMETHODIMP ImageDocument::OnStopDecode(imgIRequest *aRequest, nsresult aStatus, const PRUnichar *aStatusArg) { UpdateTitleAndCharset(); // mImageContent can be null if the document is already destroyed if (NS_FAILED(aStatus) && mStringBundle && mImageContent) { nsCAutoString src; mDocumentURI->GetSpec(src); NS_ConvertUTF8toUTF16 srcString(src); const PRUnichar* formatString[] = { srcString.get() }; nsXPIDLString errorMsg; NS_NAMED_LITERAL_STRING(str, "InvalidImage"); mStringBundle->FormatStringFromName(str.get(), formatString, 1, getter_Copies(errorMsg)); mImageContent->SetAttr(kNameSpaceID_None, nsGkAtoms::alt, errorMsg, false); } return NS_OK; } NS_IMETHODIMP ImageDocument::OnDiscard(imgIRequest *aRequest) { // mImageContent can be null if the document is already destroyed if (mImageContent) { // Remove any decoded-related styling when the image is unloaded. mImageContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::_class, true); } return NS_OK; } NS_IMETHODIMP ImageDocument::HandleEvent(nsIDOMEvent* aEvent) { nsAutoString eventType; aEvent->GetType(eventType); if (eventType.EqualsLiteral("resize")) { CheckOverflowing(false); } else if (eventType.EqualsLiteral("click") && mClickResizingEnabled) { ResetZoomLevel(); mShouldResize = true; if (mImageIsResized) { PRInt32 x = 0, y = 0; nsCOMPtr event(do_QueryInterface(aEvent)); if (event) { event->GetClientX(&x); event->GetClientY(&y); PRInt32 left = 0, top = 0; nsCOMPtr htmlElement = do_QueryInterface(mImageContent); htmlElement->GetOffsetLeft(&left); htmlElement->GetOffsetTop(&top); x -= left; y -= top; } mShouldResize = false; RestoreImageTo(x, y); } else if (mImageIsOverflowing) { ShrinkToFit(); } } return NS_OK; } nsresult ImageDocument::CreateSyntheticDocument() { // Synthesize an html document that refers to the image nsresult rv = MediaDocument::CreateSyntheticDocument(); NS_ENSURE_SUCCESS(rv, rv); // We must declare the image as a block element. If we stay as // an inline element, our parent LineBox will be inline too and // ignore the available height during reflow. // This is bad during printing, it means tall image frames won't know // the size of the paper and cannot break into continuations along // multiple pages. Element* head = GetHeadElement(); NS_ENSURE_TRUE(head, NS_ERROR_FAILURE); nsCOMPtr nodeInfo; if (nsContentUtils::IsChildOfSameType(this)) { nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::style, nullptr, kNameSpaceID_XHTML, nsIDOMNode::ELEMENT_NODE); NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY); nsRefPtr styleContent = NS_NewHTMLStyleElement(nodeInfo.forget()); NS_ENSURE_TRUE(styleContent, NS_ERROR_OUT_OF_MEMORY); styleContent->SetTextContent(NS_LITERAL_STRING("img { display: block; }")); head->AppendChildTo(styleContent, false); } // Add the image element Element* body = GetBodyElement(); if (!body) { NS_WARNING("no body on image document!"); return NS_ERROR_FAILURE; } nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::img, nullptr, kNameSpaceID_XHTML, nsIDOMNode::ELEMENT_NODE); NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY); mImageContent = NS_NewHTMLImageElement(nodeInfo.forget()); if (!mImageContent) { return NS_ERROR_OUT_OF_MEMORY; } nsCOMPtr imageLoader = do_QueryInterface(mImageContent); NS_ENSURE_TRUE(imageLoader, NS_ERROR_UNEXPECTED); nsCAutoString src; mDocumentURI->GetSpec(src); // Push a null JSContext on the stack so that code that runs within // the below code doesn't think it's being called by JS. See bug // 604262. nsCxPusher pusher; pusher.PushNull(); NS_ConvertUTF8toUTF16 srcString(src); // Make sure not to start the image load from here... imageLoader->SetLoadingEnabled(false); mImageContent->SetAttr(kNameSpaceID_None, nsGkAtoms::src, srcString, false); mImageContent->SetAttr(kNameSpaceID_None, nsGkAtoms::alt, srcString, false); body->AppendChildTo(mImageContent, false); imageLoader->SetLoadingEnabled(true); return NS_OK; } nsresult ImageDocument::CheckOverflowing(bool changeState) { /* Create a scope so that the style context gets destroyed before we might * call RebuildStyleData. Also, holding onto pointers to the * presentation through style resolution is potentially dangerous. */ { nsIPresShell *shell = GetShell(); if (!shell) { return NS_OK; } nsPresContext *context = shell->GetPresContext(); nsRect visibleArea = context->GetVisibleArea(); mVisibleWidth = nsPresContext::AppUnitsToIntCSSPixels(visibleArea.width); mVisibleHeight = nsPresContext::AppUnitsToIntCSSPixels(visibleArea.height); } bool imageWasOverflowing = mImageIsOverflowing; mImageIsOverflowing = mImageWidth > mVisibleWidth || mImageHeight > mVisibleHeight; bool windowBecameBigEnough = imageWasOverflowing && !mImageIsOverflowing; if (changeState || mShouldResize || mFirstResize || windowBecameBigEnough) { if (mImageIsOverflowing && (changeState || mShouldResize)) { ShrinkToFit(); } else if (mImageIsResized || mFirstResize || windowBecameBigEnough) { RestoreImage(); } } mFirstResize = false; return NS_OK; } void ImageDocument::UpdateTitleAndCharset() { nsCAutoString typeStr; nsCOMPtr imageRequest; nsCOMPtr imageLoader = do_QueryInterface(mImageContent); if (imageLoader) { imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, getter_AddRefs(imageRequest)); } if (imageRequest) { nsXPIDLCString mimeType; imageRequest->GetMimeType(getter_Copies(mimeType)); ToUpperCase(mimeType); nsXPIDLCString::const_iterator start, end; mimeType.BeginReading(start); mimeType.EndReading(end); nsXPIDLCString::const_iterator iter = end; if (FindInReadable(NS_LITERAL_CSTRING("IMAGE/"), start, iter) && iter != end) { // strip out "X-" if any if (*iter == 'X') { ++iter; if (iter != end && *iter == '-') { ++iter; if (iter == end) { // looks like "IMAGE/X-" is the type?? Bail out of here. mimeType.BeginReading(iter); } } else { --iter; } } typeStr = Substring(iter, end); } else { typeStr = mimeType; } } nsXPIDLString status; if (mImageIsResized) { nsAutoString ratioStr; ratioStr.AppendInt(NSToCoordFloor(GetRatio() * 100)); const PRUnichar* formatString[1] = { ratioStr.get() }; mStringBundle->FormatStringFromName(NS_LITERAL_STRING("ScaledImage").get(), formatString, 1, getter_Copies(status)); } static const char* const formatNames[4] = { "ImageTitleWithNeitherDimensionsNorFile", "ImageTitleWithoutDimensions", "ImageTitleWithDimensions", "ImageTitleWithDimensionsAndFile", }; MediaDocument::UpdateTitleAndCharset(typeStr, formatNames, mImageWidth, mImageHeight, status); } void ImageDocument::ResetZoomLevel() { nsCOMPtr docShell = do_QueryReferent(mDocumentContainer); if (docShell) { if (nsContentUtils::IsChildOfSameType(this)) { return; } nsCOMPtr cv; docShell->GetContentViewer(getter_AddRefs(cv)); nsCOMPtr mdv = do_QueryInterface(cv); if (mdv) { mdv->SetFullZoom(mOriginalZoomLevel); } } } float ImageDocument::GetZoomLevel() { float zoomLevel = mOriginalZoomLevel; nsCOMPtr docShell = do_QueryReferent(mDocumentContainer); if (docShell) { nsCOMPtr cv; docShell->GetContentViewer(getter_AddRefs(cv)); nsCOMPtr mdv = do_QueryInterface(cv); if (mdv) { mdv->GetFullZoom(&zoomLevel); } } return zoomLevel; } } // namespace dom } // namespace mozilla DOMCI_DATA(ImageDocument, mozilla::dom::ImageDocument) nsresult NS_NewImageDocument(nsIDocument** aResult) { mozilla::dom::ImageDocument* doc = new mozilla::dom::ImageDocument(); NS_ADDREF(doc); nsresult rv = doc->Init(); if (NS_FAILED(rv)) { NS_RELEASE(doc); } *aResult = doc; return rv; }