1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/html/document/src/ImageDocument.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,815 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "ImageDocument.h" 1.10 +#include "mozilla/dom/ImageDocumentBinding.h" 1.11 +#include "nsRect.h" 1.12 +#include "nsIImageLoadingContent.h" 1.13 +#include "nsGenericHTMLElement.h" 1.14 +#include "nsDocShell.h" 1.15 +#include "nsIDocumentInlines.h" 1.16 +#include "nsDOMTokenList.h" 1.17 +#include "nsIDOMHTMLImageElement.h" 1.18 +#include "nsIDOMEvent.h" 1.19 +#include "nsIDOMKeyEvent.h" 1.20 +#include "nsIDOMMouseEvent.h" 1.21 +#include "nsIDOMEventListener.h" 1.22 +#include "nsIFrame.h" 1.23 +#include "nsGkAtoms.h" 1.24 +#include "imgIRequest.h" 1.25 +#include "imgILoader.h" 1.26 +#include "imgIContainer.h" 1.27 +#include "imgINotificationObserver.h" 1.28 +#include "nsIPresShell.h" 1.29 +#include "nsPresContext.h" 1.30 +#include "nsStyleContext.h" 1.31 +#include "nsAutoPtr.h" 1.32 +#include "nsStyleSet.h" 1.33 +#include "nsIChannel.h" 1.34 +#include "nsIContentPolicy.h" 1.35 +#include "nsContentPolicyUtils.h" 1.36 +#include "nsPIDOMWindow.h" 1.37 +#include "nsIDOMElement.h" 1.38 +#include "nsIDOMHTMLElement.h" 1.39 +#include "nsError.h" 1.40 +#include "nsURILoader.h" 1.41 +#include "nsIDocShell.h" 1.42 +#include "nsIContentViewer.h" 1.43 +#include "nsIMarkupDocumentViewer.h" 1.44 +#include "nsThreadUtils.h" 1.45 +#include "nsIScrollableFrame.h" 1.46 +#include "nsContentUtils.h" 1.47 +#include "mozilla/dom/Element.h" 1.48 +#include "mozilla/Preferences.h" 1.49 +#include <algorithm> 1.50 + 1.51 +#define AUTOMATIC_IMAGE_RESIZING_PREF "browser.enable_automatic_image_resizing" 1.52 +#define CLICK_IMAGE_RESIZING_PREF "browser.enable_click_image_resizing" 1.53 +//XXX A hack needed for Firefox's site specific zoom. 1.54 +#define SITE_SPECIFIC_ZOOM "browser.zoom.siteSpecific" 1.55 + 1.56 +namespace mozilla { 1.57 +namespace dom { 1.58 + 1.59 +class ImageListener : public MediaDocumentStreamListener 1.60 +{ 1.61 +public: 1.62 + NS_DECL_NSIREQUESTOBSERVER 1.63 + 1.64 + ImageListener(ImageDocument* aDocument); 1.65 + virtual ~ImageListener(); 1.66 +}; 1.67 + 1.68 +ImageListener::ImageListener(ImageDocument* aDocument) 1.69 + : MediaDocumentStreamListener(aDocument) 1.70 +{ 1.71 +} 1.72 + 1.73 +ImageListener::~ImageListener() 1.74 +{ 1.75 +} 1.76 + 1.77 +NS_IMETHODIMP 1.78 +ImageListener::OnStartRequest(nsIRequest* request, nsISupports *ctxt) 1.79 +{ 1.80 + NS_ENSURE_TRUE(mDocument, NS_ERROR_FAILURE); 1.81 + 1.82 + ImageDocument *imgDoc = static_cast<ImageDocument*>(mDocument.get()); 1.83 + nsCOMPtr<nsIChannel> channel = do_QueryInterface(request); 1.84 + if (!channel) { 1.85 + return NS_ERROR_FAILURE; 1.86 + } 1.87 + 1.88 + nsCOMPtr<nsPIDOMWindow> domWindow = imgDoc->GetWindow(); 1.89 + NS_ENSURE_TRUE(domWindow, NS_ERROR_UNEXPECTED); 1.90 + 1.91 + // Do a ShouldProcess check to see whether to keep loading the image. 1.92 + nsCOMPtr<nsIURI> channelURI; 1.93 + channel->GetURI(getter_AddRefs(channelURI)); 1.94 + 1.95 + nsAutoCString mimeType; 1.96 + channel->GetContentType(mimeType); 1.97 + 1.98 + nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); 1.99 + nsCOMPtr<nsIPrincipal> channelPrincipal; 1.100 + if (secMan) { 1.101 + secMan->GetChannelPrincipal(channel, getter_AddRefs(channelPrincipal)); 1.102 + } 1.103 + 1.104 + int16_t decision = nsIContentPolicy::ACCEPT; 1.105 + nsresult rv = NS_CheckContentProcessPolicy(nsIContentPolicy::TYPE_IMAGE, 1.106 + channelURI, 1.107 + channelPrincipal, 1.108 + domWindow->GetFrameElementInternal(), 1.109 + mimeType, 1.110 + nullptr, 1.111 + &decision, 1.112 + nsContentUtils::GetContentPolicy(), 1.113 + secMan); 1.114 + 1.115 + if (NS_FAILED(rv) || NS_CP_REJECTED(decision)) { 1.116 + request->Cancel(NS_ERROR_CONTENT_BLOCKED); 1.117 + return NS_OK; 1.118 + } 1.119 + 1.120 + nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(imgDoc->mImageContent); 1.121 + NS_ENSURE_TRUE(imageLoader, NS_ERROR_UNEXPECTED); 1.122 + 1.123 + imageLoader->AddObserver(imgDoc); 1.124 + imgDoc->mObservingImageLoader = true; 1.125 + imageLoader->LoadImageWithChannel(channel, getter_AddRefs(mNextStream)); 1.126 + 1.127 + return MediaDocumentStreamListener::OnStartRequest(request, ctxt); 1.128 +} 1.129 + 1.130 +NS_IMETHODIMP 1.131 +ImageListener::OnStopRequest(nsIRequest* aRequest, nsISupports* aCtxt, nsresult aStatus) 1.132 +{ 1.133 + ImageDocument* imgDoc = static_cast<ImageDocument*>(mDocument.get()); 1.134 + nsContentUtils::DispatchChromeEvent(imgDoc, static_cast<nsIDocument*>(imgDoc), 1.135 + NS_LITERAL_STRING("ImageContentLoaded"), 1.136 + true, true); 1.137 + return MediaDocumentStreamListener::OnStopRequest(aRequest, aCtxt, aStatus); 1.138 +} 1.139 + 1.140 +ImageDocument::ImageDocument() 1.141 + : MediaDocument(), 1.142 + mOriginalZoomLevel(1.0) 1.143 +{ 1.144 + // NOTE! nsDocument::operator new() zeroes out all members, so don't 1.145 + // bother initializing members to 0. 1.146 +} 1.147 + 1.148 +ImageDocument::~ImageDocument() 1.149 +{ 1.150 +} 1.151 + 1.152 + 1.153 +NS_IMPL_CYCLE_COLLECTION_INHERITED(ImageDocument, MediaDocument, 1.154 + mImageContent) 1.155 + 1.156 +NS_IMPL_ADDREF_INHERITED(ImageDocument, MediaDocument) 1.157 +NS_IMPL_RELEASE_INHERITED(ImageDocument, MediaDocument) 1.158 + 1.159 +NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(ImageDocument) 1.160 + NS_INTERFACE_TABLE_INHERITED(ImageDocument, nsIImageDocument, 1.161 + imgINotificationObserver, nsIDOMEventListener) 1.162 +NS_INTERFACE_TABLE_TAIL_INHERITING(MediaDocument) 1.163 + 1.164 + 1.165 +nsresult 1.166 +ImageDocument::Init() 1.167 +{ 1.168 + nsresult rv = MediaDocument::Init(); 1.169 + NS_ENSURE_SUCCESS(rv, rv); 1.170 + 1.171 + mResizeImageByDefault = Preferences::GetBool(AUTOMATIC_IMAGE_RESIZING_PREF); 1.172 + mClickResizingEnabled = Preferences::GetBool(CLICK_IMAGE_RESIZING_PREF); 1.173 + mShouldResize = mResizeImageByDefault; 1.174 + mFirstResize = true; 1.175 + 1.176 + return NS_OK; 1.177 +} 1.178 + 1.179 +JSObject* 1.180 +ImageDocument::WrapNode(JSContext* aCx) 1.181 +{ 1.182 + return ImageDocumentBinding::Wrap(aCx, this); 1.183 +} 1.184 + 1.185 +nsresult 1.186 +ImageDocument::StartDocumentLoad(const char* aCommand, 1.187 + nsIChannel* aChannel, 1.188 + nsILoadGroup* aLoadGroup, 1.189 + nsISupports* aContainer, 1.190 + nsIStreamListener** aDocListener, 1.191 + bool aReset, 1.192 + nsIContentSink* aSink) 1.193 +{ 1.194 + nsresult rv = 1.195 + MediaDocument::StartDocumentLoad(aCommand, aChannel, aLoadGroup, aContainer, 1.196 + aDocListener, aReset, aSink); 1.197 + if (NS_FAILED(rv)) { 1.198 + return rv; 1.199 + } 1.200 + 1.201 + mOriginalZoomLevel = 1.202 + Preferences::GetBool(SITE_SPECIFIC_ZOOM, false) ? 1.0 : GetZoomLevel(); 1.203 + 1.204 + NS_ASSERTION(aDocListener, "null aDocListener"); 1.205 + *aDocListener = new ImageListener(this); 1.206 + NS_ADDREF(*aDocListener); 1.207 + 1.208 + return NS_OK; 1.209 +} 1.210 + 1.211 +void 1.212 +ImageDocument::Destroy() 1.213 +{ 1.214 + if (mImageContent) { 1.215 + // Remove our event listener from the image content. 1.216 + nsCOMPtr<EventTarget> target = do_QueryInterface(mImageContent); 1.217 + target->RemoveEventListener(NS_LITERAL_STRING("load"), this, false); 1.218 + target->RemoveEventListener(NS_LITERAL_STRING("click"), this, false); 1.219 + 1.220 + // Break reference cycle with mImageContent, if we have one 1.221 + if (mObservingImageLoader) { 1.222 + nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mImageContent); 1.223 + if (imageLoader) { 1.224 + imageLoader->RemoveObserver(this); 1.225 + } 1.226 + } 1.227 + 1.228 + mImageContent = nullptr; 1.229 + } 1.230 + 1.231 + MediaDocument::Destroy(); 1.232 +} 1.233 + 1.234 +void 1.235 +ImageDocument::SetScriptGlobalObject(nsIScriptGlobalObject* aScriptGlobalObject) 1.236 +{ 1.237 + // If the script global object is changing, we need to unhook our event 1.238 + // listeners on the window. 1.239 + nsCOMPtr<EventTarget> target; 1.240 + if (mScriptGlobalObject && 1.241 + aScriptGlobalObject != mScriptGlobalObject) { 1.242 + target = do_QueryInterface(mScriptGlobalObject); 1.243 + target->RemoveEventListener(NS_LITERAL_STRING("resize"), this, false); 1.244 + target->RemoveEventListener(NS_LITERAL_STRING("keypress"), this, 1.245 + false); 1.246 + } 1.247 + 1.248 + // Set the script global object on the superclass before doing 1.249 + // anything that might require it.... 1.250 + MediaDocument::SetScriptGlobalObject(aScriptGlobalObject); 1.251 + 1.252 + if (aScriptGlobalObject) { 1.253 + if (!GetRootElement()) { 1.254 + // Create synthetic document 1.255 +#ifdef DEBUG 1.256 + nsresult rv = 1.257 +#endif 1.258 + CreateSyntheticDocument(); 1.259 + NS_ASSERTION(NS_SUCCEEDED(rv), "failed to create synthetic document"); 1.260 + 1.261 + target = do_QueryInterface(mImageContent); 1.262 + target->AddEventListener(NS_LITERAL_STRING("load"), this, false); 1.263 + target->AddEventListener(NS_LITERAL_STRING("click"), this, false); 1.264 + } 1.265 + 1.266 + target = do_QueryInterface(aScriptGlobalObject); 1.267 + target->AddEventListener(NS_LITERAL_STRING("resize"), this, false); 1.268 + target->AddEventListener(NS_LITERAL_STRING("keypress"), this, false); 1.269 + 1.270 + if (GetReadyStateEnum() != nsIDocument::READYSTATE_COMPLETE) { 1.271 + LinkStylesheet(NS_LITERAL_STRING("resource://gre/res/ImageDocument.css")); 1.272 + if (!nsContentUtils::IsChildOfSameType(this)) { 1.273 + LinkStylesheet(NS_LITERAL_STRING("resource://gre/res/TopLevelImageDocument.css")); 1.274 + LinkStylesheet(NS_LITERAL_STRING("chrome://global/skin/media/TopLevelImageDocument.css")); 1.275 + } 1.276 + } 1.277 + BecomeInteractive(); 1.278 + } 1.279 +} 1.280 + 1.281 +void 1.282 +ImageDocument::OnPageShow(bool aPersisted, 1.283 + EventTarget* aDispatchStartTarget) 1.284 +{ 1.285 + if (aPersisted) { 1.286 + mOriginalZoomLevel = 1.287 + Preferences::GetBool(SITE_SPECIFIC_ZOOM, false) ? 1.0 : GetZoomLevel(); 1.288 + } 1.289 + MediaDocument::OnPageShow(aPersisted, aDispatchStartTarget); 1.290 +} 1.291 + 1.292 +NS_IMETHODIMP 1.293 +ImageDocument::GetImageResizingEnabled(bool* aImageResizingEnabled) 1.294 +{ 1.295 + *aImageResizingEnabled = ImageResizingEnabled(); 1.296 + return NS_OK; 1.297 +} 1.298 + 1.299 +NS_IMETHODIMP 1.300 +ImageDocument::GetImageIsOverflowing(bool* aImageIsOverflowing) 1.301 +{ 1.302 + *aImageIsOverflowing = ImageIsOverflowing(); 1.303 + return NS_OK; 1.304 +} 1.305 + 1.306 +NS_IMETHODIMP 1.307 +ImageDocument::GetImageIsResized(bool* aImageIsResized) 1.308 +{ 1.309 + *aImageIsResized = ImageIsResized(); 1.310 + return NS_OK; 1.311 +} 1.312 + 1.313 +already_AddRefed<imgIRequest> 1.314 +ImageDocument::GetImageRequest(ErrorResult& aRv) 1.315 +{ 1.316 + nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mImageContent); 1.317 + nsCOMPtr<imgIRequest> imageRequest; 1.318 + if (imageLoader) { 1.319 + aRv = imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, 1.320 + getter_AddRefs(imageRequest)); 1.321 + } 1.322 + return imageRequest.forget(); 1.323 +} 1.324 + 1.325 +NS_IMETHODIMP 1.326 +ImageDocument::GetImageRequest(imgIRequest** aImageRequest) 1.327 +{ 1.328 + ErrorResult rv; 1.329 + *aImageRequest = GetImageRequest(rv).take(); 1.330 + return rv.ErrorCode(); 1.331 +} 1.332 + 1.333 +void 1.334 +ImageDocument::ShrinkToFit() 1.335 +{ 1.336 + if (!mImageContent) { 1.337 + return; 1.338 + } 1.339 + if (GetZoomLevel() != mOriginalZoomLevel && mImageIsResized && 1.340 + !nsContentUtils::IsChildOfSameType(this)) { 1.341 + return; 1.342 + } 1.343 + 1.344 + // Keep image content alive while changing the attributes. 1.345 + nsCOMPtr<nsIContent> imageContent = mImageContent; 1.346 + nsCOMPtr<nsIDOMHTMLImageElement> image = do_QueryInterface(mImageContent); 1.347 + image->SetWidth(std::max(1, NSToCoordFloor(GetRatio() * mImageWidth))); 1.348 + image->SetHeight(std::max(1, NSToCoordFloor(GetRatio() * mImageHeight))); 1.349 + 1.350 + // The view might have been scrolled when zooming in, scroll back to the 1.351 + // origin now that we're showing a shrunk-to-window version. 1.352 + ScrollImageTo(0, 0, false); 1.353 + 1.354 + if (!mImageContent) { 1.355 + // ScrollImageTo flush destroyed our content. 1.356 + return; 1.357 + } 1.358 + 1.359 + SetModeClass(eShrinkToFit); 1.360 + 1.361 + mImageIsResized = true; 1.362 + 1.363 + UpdateTitleAndCharset(); 1.364 +} 1.365 + 1.366 +NS_IMETHODIMP 1.367 +ImageDocument::DOMShrinkToFit() 1.368 +{ 1.369 + ShrinkToFit(); 1.370 + return NS_OK; 1.371 +} 1.372 + 1.373 +NS_IMETHODIMP 1.374 +ImageDocument::DOMRestoreImageTo(int32_t aX, int32_t aY) 1.375 +{ 1.376 + RestoreImageTo(aX, aY); 1.377 + return NS_OK; 1.378 +} 1.379 + 1.380 +void 1.381 +ImageDocument::ScrollImageTo(int32_t aX, int32_t aY, bool restoreImage) 1.382 +{ 1.383 + float ratio = GetRatio(); 1.384 + 1.385 + if (restoreImage) { 1.386 + RestoreImage(); 1.387 + FlushPendingNotifications(Flush_Layout); 1.388 + } 1.389 + 1.390 + nsCOMPtr<nsIPresShell> shell = GetShell(); 1.391 + if (!shell) 1.392 + return; 1.393 + 1.394 + nsIScrollableFrame* sf = shell->GetRootScrollFrameAsScrollable(); 1.395 + if (!sf) 1.396 + return; 1.397 + 1.398 + nsRect portRect = sf->GetScrollPortRect(); 1.399 + sf->ScrollTo(nsPoint(nsPresContext::CSSPixelsToAppUnits(aX/ratio) - portRect.width/2, 1.400 + nsPresContext::CSSPixelsToAppUnits(aY/ratio) - portRect.height/2), 1.401 + nsIScrollableFrame::INSTANT); 1.402 +} 1.403 + 1.404 +void 1.405 +ImageDocument::RestoreImage() 1.406 +{ 1.407 + if (!mImageContent) { 1.408 + return; 1.409 + } 1.410 + // Keep image content alive while changing the attributes. 1.411 + nsCOMPtr<nsIContent> imageContent = mImageContent; 1.412 + imageContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::width, true); 1.413 + imageContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::height, true); 1.414 + 1.415 + if (mImageIsOverflowing) { 1.416 + SetModeClass(eOverflowing); 1.417 + } 1.418 + else { 1.419 + SetModeClass(eNone); 1.420 + } 1.421 + 1.422 + mImageIsResized = false; 1.423 + 1.424 + UpdateTitleAndCharset(); 1.425 +} 1.426 + 1.427 +NS_IMETHODIMP 1.428 +ImageDocument::DOMRestoreImage() 1.429 +{ 1.430 + RestoreImage(); 1.431 + return NS_OK; 1.432 +} 1.433 + 1.434 +void 1.435 +ImageDocument::ToggleImageSize() 1.436 +{ 1.437 + mShouldResize = true; 1.438 + if (mImageIsResized) { 1.439 + mShouldResize = false; 1.440 + ResetZoomLevel(); 1.441 + RestoreImage(); 1.442 + } 1.443 + else if (mImageIsOverflowing) { 1.444 + ResetZoomLevel(); 1.445 + ShrinkToFit(); 1.446 + } 1.447 +} 1.448 + 1.449 +NS_IMETHODIMP 1.450 +ImageDocument::DOMToggleImageSize() 1.451 +{ 1.452 + ToggleImageSize(); 1.453 + return NS_OK; 1.454 +} 1.455 + 1.456 +NS_IMETHODIMP 1.457 +ImageDocument::Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aData) 1.458 +{ 1.459 + if (aType == imgINotificationObserver::SIZE_AVAILABLE) { 1.460 + nsCOMPtr<imgIContainer> image; 1.461 + aRequest->GetImage(getter_AddRefs(image)); 1.462 + return OnStartContainer(aRequest, image); 1.463 + } 1.464 + 1.465 + nsDOMTokenList* classList = mImageContent->AsElement()->GetClassList(); 1.466 + mozilla::ErrorResult rv; 1.467 + if (aType == imgINotificationObserver::DECODE_COMPLETE) { 1.468 + if (mImageContent && !nsContentUtils::IsChildOfSameType(this)) { 1.469 + // Update the background-color of the image only after the 1.470 + // image has been decoded to prevent flashes of just the 1.471 + // background-color. 1.472 + classList->Add(NS_LITERAL_STRING("decoded"), rv); 1.473 + NS_ENSURE_SUCCESS(rv.ErrorCode(), rv.ErrorCode()); 1.474 + } 1.475 + } 1.476 + 1.477 + if (aType == imgINotificationObserver::DISCARD) { 1.478 + // mImageContent can be null if the document is already destroyed 1.479 + if (mImageContent && !nsContentUtils::IsChildOfSameType(this)) { 1.480 + // Remove any decoded-related styling when the image is unloaded. 1.481 + classList->Remove(NS_LITERAL_STRING("decoded"), rv); 1.482 + NS_ENSURE_SUCCESS(rv.ErrorCode(), rv.ErrorCode()); 1.483 + } 1.484 + } 1.485 + 1.486 + if (aType == imgINotificationObserver::LOAD_COMPLETE) { 1.487 + uint32_t reqStatus; 1.488 + aRequest->GetImageStatus(&reqStatus); 1.489 + nsresult status = 1.490 + reqStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK; 1.491 + return OnStopRequest(aRequest, status); 1.492 + } 1.493 + 1.494 + return NS_OK; 1.495 +} 1.496 + 1.497 +void 1.498 +ImageDocument::SetModeClass(eModeClasses mode) 1.499 +{ 1.500 + nsDOMTokenList* classList = mImageContent->AsElement()->GetClassList(); 1.501 + mozilla::ErrorResult rv; 1.502 + 1.503 + if (mode == eShrinkToFit) { 1.504 + classList->Add(NS_LITERAL_STRING("shrinkToFit"), rv); 1.505 + } else { 1.506 + classList->Remove(NS_LITERAL_STRING("shrinkToFit"), rv); 1.507 + } 1.508 + 1.509 + if (mode == eOverflowing) { 1.510 + classList->Add(NS_LITERAL_STRING("overflowing"), rv); 1.511 + } else { 1.512 + classList->Remove(NS_LITERAL_STRING("overflowing"), rv); 1.513 + } 1.514 +} 1.515 + 1.516 +nsresult 1.517 +ImageDocument::OnStartContainer(imgIRequest* aRequest, imgIContainer* aImage) 1.518 +{ 1.519 + // Styles have not yet been applied, so we don't know the final size. For now, 1.520 + // default to the image's intrinsic size. 1.521 + aImage->GetWidth(&mImageWidth); 1.522 + aImage->GetHeight(&mImageHeight); 1.523 + 1.524 + nsCOMPtr<nsIRunnable> runnable = 1.525 + NS_NewRunnableMethod(this, &ImageDocument::DefaultCheckOverflowing); 1.526 + nsContentUtils::AddScriptRunner(runnable); 1.527 + UpdateTitleAndCharset(); 1.528 + 1.529 + return NS_OK; 1.530 +} 1.531 + 1.532 +nsresult 1.533 +ImageDocument::OnStopRequest(imgIRequest *aRequest, 1.534 + nsresult aStatus) 1.535 +{ 1.536 + UpdateTitleAndCharset(); 1.537 + 1.538 + // mImageContent can be null if the document is already destroyed 1.539 + if (NS_FAILED(aStatus) && mStringBundle && mImageContent) { 1.540 + nsAutoCString src; 1.541 + mDocumentURI->GetSpec(src); 1.542 + NS_ConvertUTF8toUTF16 srcString(src); 1.543 + const char16_t* formatString[] = { srcString.get() }; 1.544 + nsXPIDLString errorMsg; 1.545 + NS_NAMED_LITERAL_STRING(str, "InvalidImage"); 1.546 + mStringBundle->FormatStringFromName(str.get(), formatString, 1, 1.547 + getter_Copies(errorMsg)); 1.548 + 1.549 + mImageContent->SetAttr(kNameSpaceID_None, nsGkAtoms::alt, errorMsg, false); 1.550 + } 1.551 + 1.552 + return NS_OK; 1.553 +} 1.554 + 1.555 +NS_IMETHODIMP 1.556 +ImageDocument::HandleEvent(nsIDOMEvent* aEvent) 1.557 +{ 1.558 + nsAutoString eventType; 1.559 + aEvent->GetType(eventType); 1.560 + if (eventType.EqualsLiteral("resize")) { 1.561 + CheckOverflowing(false); 1.562 + } 1.563 + else if (eventType.EqualsLiteral("click") && mClickResizingEnabled) { 1.564 + ResetZoomLevel(); 1.565 + mShouldResize = true; 1.566 + if (mImageIsResized) { 1.567 + int32_t x = 0, y = 0; 1.568 + nsCOMPtr<nsIDOMMouseEvent> event(do_QueryInterface(aEvent)); 1.569 + if (event) { 1.570 + event->GetClientX(&x); 1.571 + event->GetClientY(&y); 1.572 + int32_t left = 0, top = 0; 1.573 + nsCOMPtr<nsIDOMHTMLElement> htmlElement = 1.574 + do_QueryInterface(mImageContent); 1.575 + htmlElement->GetOffsetLeft(&left); 1.576 + htmlElement->GetOffsetTop(&top); 1.577 + x -= left; 1.578 + y -= top; 1.579 + } 1.580 + mShouldResize = false; 1.581 + RestoreImageTo(x, y); 1.582 + } 1.583 + else if (mImageIsOverflowing) { 1.584 + ShrinkToFit(); 1.585 + } 1.586 + } else if (eventType.EqualsLiteral("load")) { 1.587 + UpdateSizeFromLayout(); 1.588 + } 1.589 + 1.590 + return NS_OK; 1.591 +} 1.592 + 1.593 +void 1.594 +ImageDocument::UpdateSizeFromLayout() 1.595 +{ 1.596 + // Pull an updated size from the content frame to account for any size 1.597 + // change due to CSS properties like |image-orientation|. 1.598 + Element* contentElement = mImageContent->AsElement(); 1.599 + if (!contentElement) { 1.600 + return; 1.601 + } 1.602 + 1.603 + nsIFrame* contentFrame = contentElement->GetPrimaryFrame(Flush_Frames); 1.604 + if (!contentFrame) { 1.605 + return; 1.606 + } 1.607 + 1.608 + nsIntSize oldSize(mImageWidth, mImageHeight); 1.609 + IntrinsicSize newSize = contentFrame->GetIntrinsicSize(); 1.610 + 1.611 + if (newSize.width.GetUnit() == eStyleUnit_Coord) { 1.612 + mImageWidth = nsPresContext::AppUnitsToFloatCSSPixels(newSize.width.GetCoordValue()); 1.613 + } 1.614 + if (newSize.height.GetUnit() == eStyleUnit_Coord) { 1.615 + mImageHeight = nsPresContext::AppUnitsToFloatCSSPixels(newSize.height.GetCoordValue()); 1.616 + } 1.617 + 1.618 + // Ensure that our information about overflow is up-to-date if needed. 1.619 + if (mImageWidth != oldSize.width || mImageHeight != oldSize.height) { 1.620 + CheckOverflowing(false); 1.621 + } 1.622 +} 1.623 + 1.624 +nsresult 1.625 +ImageDocument::CreateSyntheticDocument() 1.626 +{ 1.627 + // Synthesize an html document that refers to the image 1.628 + nsresult rv = MediaDocument::CreateSyntheticDocument(); 1.629 + NS_ENSURE_SUCCESS(rv, rv); 1.630 + 1.631 + // Add the image element 1.632 + Element* body = GetBodyElement(); 1.633 + if (!body) { 1.634 + NS_WARNING("no body on image document!"); 1.635 + return NS_ERROR_FAILURE; 1.636 + } 1.637 + 1.638 + nsCOMPtr<nsINodeInfo> nodeInfo; 1.639 + nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::img, nullptr, 1.640 + kNameSpaceID_XHTML, 1.641 + nsIDOMNode::ELEMENT_NODE); 1.642 + 1.643 + mImageContent = NS_NewHTMLImageElement(nodeInfo.forget()); 1.644 + if (!mImageContent) { 1.645 + return NS_ERROR_OUT_OF_MEMORY; 1.646 + } 1.647 + nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mImageContent); 1.648 + NS_ENSURE_TRUE(imageLoader, NS_ERROR_UNEXPECTED); 1.649 + 1.650 + nsAutoCString src; 1.651 + mDocumentURI->GetSpec(src); 1.652 + 1.653 + NS_ConvertUTF8toUTF16 srcString(src); 1.654 + // Make sure not to start the image load from here... 1.655 + imageLoader->SetLoadingEnabled(false); 1.656 + mImageContent->SetAttr(kNameSpaceID_None, nsGkAtoms::src, srcString, false); 1.657 + mImageContent->SetAttr(kNameSpaceID_None, nsGkAtoms::alt, srcString, false); 1.658 + 1.659 + body->AppendChildTo(mImageContent, false); 1.660 + imageLoader->SetLoadingEnabled(true); 1.661 + 1.662 + return NS_OK; 1.663 +} 1.664 + 1.665 +nsresult 1.666 +ImageDocument::CheckOverflowing(bool changeState) 1.667 +{ 1.668 + /* Create a scope so that the style context gets destroyed before we might 1.669 + * call RebuildStyleData. Also, holding onto pointers to the 1.670 + * presentation through style resolution is potentially dangerous. 1.671 + */ 1.672 + { 1.673 + nsIPresShell *shell = GetShell(); 1.674 + if (!shell) { 1.675 + return NS_OK; 1.676 + } 1.677 + 1.678 + nsPresContext *context = shell->GetPresContext(); 1.679 + nsRect visibleArea = context->GetVisibleArea(); 1.680 + 1.681 + mVisibleWidth = nsPresContext::AppUnitsToFloatCSSPixels(visibleArea.width); 1.682 + mVisibleHeight = nsPresContext::AppUnitsToFloatCSSPixels(visibleArea.height); 1.683 + } 1.684 + 1.685 + bool imageWasOverflowing = mImageIsOverflowing; 1.686 + mImageIsOverflowing = 1.687 + mImageWidth > mVisibleWidth || mImageHeight > mVisibleHeight; 1.688 + bool windowBecameBigEnough = imageWasOverflowing && !mImageIsOverflowing; 1.689 + 1.690 + if (changeState || mShouldResize || mFirstResize || 1.691 + windowBecameBigEnough) { 1.692 + if (mImageIsOverflowing && (changeState || mShouldResize)) { 1.693 + ShrinkToFit(); 1.694 + } 1.695 + else if (mImageIsResized || mFirstResize || windowBecameBigEnough) { 1.696 + RestoreImage(); 1.697 + } 1.698 + } 1.699 + mFirstResize = false; 1.700 + 1.701 + return NS_OK; 1.702 +} 1.703 + 1.704 +void 1.705 +ImageDocument::UpdateTitleAndCharset() 1.706 +{ 1.707 + nsAutoCString typeStr; 1.708 + nsCOMPtr<imgIRequest> imageRequest; 1.709 + nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mImageContent); 1.710 + if (imageLoader) { 1.711 + imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, 1.712 + getter_AddRefs(imageRequest)); 1.713 + } 1.714 + 1.715 + if (imageRequest) { 1.716 + nsXPIDLCString mimeType; 1.717 + imageRequest->GetMimeType(getter_Copies(mimeType)); 1.718 + ToUpperCase(mimeType); 1.719 + nsXPIDLCString::const_iterator start, end; 1.720 + mimeType.BeginReading(start); 1.721 + mimeType.EndReading(end); 1.722 + nsXPIDLCString::const_iterator iter = end; 1.723 + if (FindInReadable(NS_LITERAL_CSTRING("IMAGE/"), start, iter) && 1.724 + iter != end) { 1.725 + // strip out "X-" if any 1.726 + if (*iter == 'X') { 1.727 + ++iter; 1.728 + if (iter != end && *iter == '-') { 1.729 + ++iter; 1.730 + if (iter == end) { 1.731 + // looks like "IMAGE/X-" is the type?? Bail out of here. 1.732 + mimeType.BeginReading(iter); 1.733 + } 1.734 + } else { 1.735 + --iter; 1.736 + } 1.737 + } 1.738 + typeStr = Substring(iter, end); 1.739 + } else { 1.740 + typeStr = mimeType; 1.741 + } 1.742 + } 1.743 + 1.744 + nsXPIDLString status; 1.745 + if (mImageIsResized) { 1.746 + nsAutoString ratioStr; 1.747 + ratioStr.AppendInt(NSToCoordFloor(GetRatio() * 100)); 1.748 + 1.749 + const char16_t* formatString[1] = { ratioStr.get() }; 1.750 + mStringBundle->FormatStringFromName(MOZ_UTF16("ScaledImage"), 1.751 + formatString, 1, 1.752 + getter_Copies(status)); 1.753 + } 1.754 + 1.755 + static const char* const formatNames[4] = 1.756 + { 1.757 + "ImageTitleWithNeitherDimensionsNorFile", 1.758 + "ImageTitleWithoutDimensions", 1.759 + "ImageTitleWithDimensions2", 1.760 + "ImageTitleWithDimensions2AndFile", 1.761 + }; 1.762 + 1.763 + MediaDocument::UpdateTitleAndCharset(typeStr, formatNames, 1.764 + mImageWidth, mImageHeight, status); 1.765 +} 1.766 + 1.767 +void 1.768 +ImageDocument::ResetZoomLevel() 1.769 +{ 1.770 + nsCOMPtr<nsIDocShell> docShell(mDocumentContainer); 1.771 + if (docShell) { 1.772 + if (nsContentUtils::IsChildOfSameType(this)) { 1.773 + return; 1.774 + } 1.775 + 1.776 + nsCOMPtr<nsIContentViewer> cv; 1.777 + docShell->GetContentViewer(getter_AddRefs(cv)); 1.778 + nsCOMPtr<nsIMarkupDocumentViewer> mdv = do_QueryInterface(cv); 1.779 + if (mdv) { 1.780 + mdv->SetFullZoom(mOriginalZoomLevel); 1.781 + } 1.782 + } 1.783 +} 1.784 + 1.785 +float 1.786 +ImageDocument::GetZoomLevel() 1.787 +{ 1.788 + float zoomLevel = mOriginalZoomLevel; 1.789 + nsCOMPtr<nsIDocShell> docShell(mDocumentContainer); 1.790 + if (docShell) { 1.791 + nsCOMPtr<nsIContentViewer> cv; 1.792 + docShell->GetContentViewer(getter_AddRefs(cv)); 1.793 + nsCOMPtr<nsIMarkupDocumentViewer> mdv = do_QueryInterface(cv); 1.794 + if (mdv) { 1.795 + mdv->GetFullZoom(&zoomLevel); 1.796 + } 1.797 + } 1.798 + return zoomLevel; 1.799 +} 1.800 + 1.801 +} // namespace dom 1.802 +} // namespace mozilla 1.803 + 1.804 +nsresult 1.805 +NS_NewImageDocument(nsIDocument** aResult) 1.806 +{ 1.807 + mozilla::dom::ImageDocument* doc = new mozilla::dom::ImageDocument(); 1.808 + NS_ADDREF(doc); 1.809 + 1.810 + nsresult rv = doc->Init(); 1.811 + if (NS_FAILED(rv)) { 1.812 + NS_RELEASE(doc); 1.813 + } 1.814 + 1.815 + *aResult = doc; 1.816 + 1.817 + return rv; 1.818 +}