content/html/document/src/ImageDocument.cpp

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

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

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

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

mercurial