layout/style/ImageLoader.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 /* A class that handles style system image loads (other image loads are handled
michael@0 6 * by the nodes in the content tree).
michael@0 7 */
michael@0 8
michael@0 9 #include "mozilla/css/ImageLoader.h"
michael@0 10 #include "nsContentUtils.h"
michael@0 11 #include "nsLayoutUtils.h"
michael@0 12 #include "nsError.h"
michael@0 13 #include "nsDisplayList.h"
michael@0 14 #include "FrameLayerBuilder.h"
michael@0 15 #include "nsSVGEffects.h"
michael@0 16 #include "imgIContainer.h"
michael@0 17
michael@0 18 namespace mozilla {
michael@0 19 namespace css {
michael@0 20
michael@0 21 /* static */ PLDHashOperator
michael@0 22 ImageLoader::SetAnimationModeEnumerator(nsISupports* aKey, FrameSet* aValue,
michael@0 23 void* aClosure)
michael@0 24 {
michael@0 25 imgIRequest* request = static_cast<imgIRequest*>(aKey);
michael@0 26
michael@0 27 uint16_t* mode = static_cast<uint16_t*>(aClosure);
michael@0 28
michael@0 29 #ifdef DEBUG
michael@0 30 {
michael@0 31 nsCOMPtr<imgIRequest> debugRequest = do_QueryInterface(aKey);
michael@0 32 NS_ASSERTION(debugRequest == request, "This is bad");
michael@0 33 }
michael@0 34 #endif
michael@0 35
michael@0 36 nsCOMPtr<imgIContainer> container;
michael@0 37 request->GetImage(getter_AddRefs(container));
michael@0 38 if (!container) {
michael@0 39 return PL_DHASH_NEXT;
michael@0 40 }
michael@0 41
michael@0 42 // This can fail if the image is in error, and we don't care.
michael@0 43 container->SetAnimationMode(*mode);
michael@0 44
michael@0 45 return PL_DHASH_NEXT;
michael@0 46 }
michael@0 47
michael@0 48 static PLDHashOperator
michael@0 49 ClearImageHashSet(nsPtrHashKey<ImageLoader::Image>* aKey, void* aClosure)
michael@0 50 {
michael@0 51 nsIDocument* doc = static_cast<nsIDocument*>(aClosure);
michael@0 52 ImageLoader::Image* image = aKey->GetKey();
michael@0 53
michael@0 54 imgIRequest* request = image->mRequests.GetWeak(doc);
michael@0 55 if (request) {
michael@0 56 request->CancelAndForgetObserver(NS_BINDING_ABORTED);
michael@0 57 }
michael@0 58
michael@0 59 image->mRequests.Remove(doc);
michael@0 60
michael@0 61 return PL_DHASH_REMOVE;
michael@0 62 }
michael@0 63
michael@0 64 void
michael@0 65 ImageLoader::DropDocumentReference()
michael@0 66 {
michael@0 67 ClearFrames();
michael@0 68 mImages.EnumerateEntries(&ClearImageHashSet, mDocument);
michael@0 69 mDocument = nullptr;
michael@0 70 }
michael@0 71
michael@0 72 void
michael@0 73 ImageLoader::AssociateRequestToFrame(imgIRequest* aRequest,
michael@0 74 nsIFrame* aFrame)
michael@0 75 {
michael@0 76 nsCOMPtr<imgINotificationObserver> observer;
michael@0 77 aRequest->GetNotificationObserver(getter_AddRefs(observer));
michael@0 78 if (!observer) {
michael@0 79 // The request has already been canceled, so ignore it. This is ok because
michael@0 80 // we're not going to get any more notifications from a canceled request.
michael@0 81 return;
michael@0 82 }
michael@0 83
michael@0 84 MOZ_ASSERT(observer == this);
michael@0 85
michael@0 86 FrameSet* frameSet = nullptr;
michael@0 87 if (mRequestToFrameMap.Get(aRequest, &frameSet)) {
michael@0 88 NS_ASSERTION(frameSet, "This should never be null!");
michael@0 89 }
michael@0 90
michael@0 91 if (!frameSet) {
michael@0 92 nsAutoPtr<FrameSet> newFrameSet(new FrameSet());
michael@0 93
michael@0 94 mRequestToFrameMap.Put(aRequest, newFrameSet);
michael@0 95 frameSet = newFrameSet.forget();
michael@0 96
michael@0 97 nsPresContext* presContext = GetPresContext();
michael@0 98 if (presContext) {
michael@0 99 nsLayoutUtils::RegisterImageRequestIfAnimated(presContext,
michael@0 100 aRequest,
michael@0 101 nullptr);
michael@0 102 }
michael@0 103 }
michael@0 104
michael@0 105 RequestSet* requestSet = nullptr;
michael@0 106 if (mFrameToRequestMap.Get(aFrame, &requestSet)) {
michael@0 107 NS_ASSERTION(requestSet, "This should never be null");
michael@0 108 }
michael@0 109
michael@0 110 if (!requestSet) {
michael@0 111 nsAutoPtr<RequestSet> newRequestSet(new RequestSet());
michael@0 112
michael@0 113 mFrameToRequestMap.Put(aFrame, newRequestSet);
michael@0 114 requestSet = newRequestSet.forget();
michael@0 115 }
michael@0 116
michael@0 117 // Add these to the sets, but only if they're not already there.
michael@0 118 uint32_t i = frameSet->IndexOfFirstElementGt(aFrame);
michael@0 119 if (i == 0 || aFrame != frameSet->ElementAt(i-1)) {
michael@0 120 frameSet->InsertElementAt(i, aFrame);
michael@0 121 }
michael@0 122 i = requestSet->IndexOfFirstElementGt(aRequest);
michael@0 123 if (i == 0 || aRequest != requestSet->ElementAt(i-1)) {
michael@0 124 requestSet->InsertElementAt(i, aRequest);
michael@0 125 }
michael@0 126 }
michael@0 127
michael@0 128 void
michael@0 129 ImageLoader::MaybeRegisterCSSImage(ImageLoader::Image* aImage)
michael@0 130 {
michael@0 131 NS_ASSERTION(aImage, "This should never be null!");
michael@0 132
michael@0 133 bool found = false;
michael@0 134 aImage->mRequests.GetWeak(mDocument, &found);
michael@0 135 if (found) {
michael@0 136 // This document already has a request.
michael@0 137 return;
michael@0 138 }
michael@0 139
michael@0 140 imgRequestProxy* canonicalRequest = aImage->mRequests.GetWeak(nullptr);
michael@0 141 if (!canonicalRequest) {
michael@0 142 // The image was blocked or something.
michael@0 143 return;
michael@0 144 }
michael@0 145
michael@0 146 nsRefPtr<imgRequestProxy> request;
michael@0 147
michael@0 148 // Ignore errors here. If cloning fails for some reason we'll put a null
michael@0 149 // entry in the hash and we won't keep trying to clone.
michael@0 150 mInClone = true;
michael@0 151 canonicalRequest->Clone(this, getter_AddRefs(request));
michael@0 152 mInClone = false;
michael@0 153
michael@0 154 aImage->mRequests.Put(mDocument, request);
michael@0 155
michael@0 156 AddImage(aImage);
michael@0 157 }
michael@0 158
michael@0 159 void
michael@0 160 ImageLoader::DeregisterCSSImage(ImageLoader::Image* aImage)
michael@0 161 {
michael@0 162 RemoveImage(aImage);
michael@0 163 }
michael@0 164
michael@0 165 void
michael@0 166 ImageLoader::DisassociateRequestFromFrame(imgIRequest* aRequest,
michael@0 167 nsIFrame* aFrame)
michael@0 168 {
michael@0 169 FrameSet* frameSet = nullptr;
michael@0 170 RequestSet* requestSet = nullptr;
michael@0 171
michael@0 172 #ifdef DEBUG
michael@0 173 {
michael@0 174 nsCOMPtr<imgINotificationObserver> observer;
michael@0 175 aRequest->GetNotificationObserver(getter_AddRefs(observer));
michael@0 176 MOZ_ASSERT(!observer || observer == this);
michael@0 177 }
michael@0 178 #endif
michael@0 179
michael@0 180 mRequestToFrameMap.Get(aRequest, &frameSet);
michael@0 181 mFrameToRequestMap.Get(aFrame, &requestSet);
michael@0 182
michael@0 183 if (frameSet) {
michael@0 184 frameSet->RemoveElementSorted(aFrame);
michael@0 185 }
michael@0 186 if (requestSet) {
michael@0 187 requestSet->RemoveElementSorted(aRequest);
michael@0 188 }
michael@0 189
michael@0 190 if (frameSet && !frameSet->Length()) {
michael@0 191 mRequestToFrameMap.Remove(aRequest);
michael@0 192
michael@0 193 nsPresContext* presContext = GetPresContext();
michael@0 194 if (presContext) {
michael@0 195 nsLayoutUtils::DeregisterImageRequest(presContext,
michael@0 196 aRequest,
michael@0 197 nullptr);
michael@0 198 }
michael@0 199 }
michael@0 200
michael@0 201 if (requestSet && !requestSet->Length()) {
michael@0 202 mFrameToRequestMap.Remove(aFrame);
michael@0 203 }
michael@0 204 }
michael@0 205
michael@0 206 void
michael@0 207 ImageLoader::DropRequestsForFrame(nsIFrame* aFrame)
michael@0 208 {
michael@0 209 RequestSet* requestSet = nullptr;
michael@0 210 if (!mFrameToRequestMap.Get(aFrame, &requestSet)) {
michael@0 211 return;
michael@0 212 }
michael@0 213
michael@0 214 NS_ASSERTION(requestSet, "This should never be null");
michael@0 215
michael@0 216 RequestSet frozenRequestSet(*requestSet);
michael@0 217 for (RequestSet::size_type i = frozenRequestSet.Length(); i != 0; --i) {
michael@0 218 imgIRequest* request = frozenRequestSet.ElementAt(i - 1);
michael@0 219
michael@0 220 DisassociateRequestFromFrame(request, aFrame);
michael@0 221 }
michael@0 222 }
michael@0 223
michael@0 224 void
michael@0 225 ImageLoader::SetAnimationMode(uint16_t aMode)
michael@0 226 {
michael@0 227 NS_ASSERTION(aMode == imgIContainer::kNormalAnimMode ||
michael@0 228 aMode == imgIContainer::kDontAnimMode ||
michael@0 229 aMode == imgIContainer::kLoopOnceAnimMode,
michael@0 230 "Wrong Animation Mode is being set!");
michael@0 231
michael@0 232 mRequestToFrameMap.EnumerateRead(SetAnimationModeEnumerator, &aMode);
michael@0 233 }
michael@0 234
michael@0 235 void
michael@0 236 ImageLoader::ClearFrames()
michael@0 237 {
michael@0 238 mRequestToFrameMap.Clear();
michael@0 239 mFrameToRequestMap.Clear();
michael@0 240 }
michael@0 241
michael@0 242 void
michael@0 243 ImageLoader::LoadImage(nsIURI* aURI, nsIPrincipal* aOriginPrincipal,
michael@0 244 nsIURI* aReferrer, ImageLoader::Image* aImage)
michael@0 245 {
michael@0 246 NS_ASSERTION(aImage->mRequests.Count() == 0, "Huh?");
michael@0 247
michael@0 248 aImage->mRequests.Put(nullptr, nullptr);
michael@0 249
michael@0 250 if (!aURI) {
michael@0 251 return;
michael@0 252 }
michael@0 253
michael@0 254 if (!nsContentUtils::CanLoadImage(aURI, mDocument, mDocument,
michael@0 255 aOriginPrincipal)) {
michael@0 256 return;
michael@0 257 }
michael@0 258
michael@0 259 nsRefPtr<imgRequestProxy> request;
michael@0 260 nsContentUtils::LoadImage(aURI, mDocument, aOriginPrincipal, aReferrer,
michael@0 261 nullptr, nsIRequest::LOAD_NORMAL,
michael@0 262 NS_LITERAL_STRING("css"),
michael@0 263 getter_AddRefs(request));
michael@0 264
michael@0 265 if (!request) {
michael@0 266 return;
michael@0 267 }
michael@0 268
michael@0 269 nsRefPtr<imgRequestProxy> clonedRequest;
michael@0 270 mInClone = true;
michael@0 271 nsresult rv = request->Clone(this, getter_AddRefs(clonedRequest));
michael@0 272 mInClone = false;
michael@0 273
michael@0 274 if (NS_FAILED(rv)) {
michael@0 275 return;
michael@0 276 }
michael@0 277
michael@0 278 aImage->mRequests.Put(nullptr, request);
michael@0 279 aImage->mRequests.Put(mDocument, clonedRequest);
michael@0 280
michael@0 281 AddImage(aImage);
michael@0 282 }
michael@0 283
michael@0 284 void
michael@0 285 ImageLoader::AddImage(ImageLoader::Image* aImage)
michael@0 286 {
michael@0 287 NS_ASSERTION(!mImages.Contains(aImage), "Huh?");
michael@0 288 if (!mImages.PutEntry(aImage)) {
michael@0 289 NS_RUNTIMEABORT("OOM");
michael@0 290 }
michael@0 291 }
michael@0 292
michael@0 293 void
michael@0 294 ImageLoader::RemoveImage(ImageLoader::Image* aImage)
michael@0 295 {
michael@0 296 NS_ASSERTION(mImages.Contains(aImage), "Huh?");
michael@0 297 mImages.RemoveEntry(aImage);
michael@0 298 }
michael@0 299
michael@0 300 nsPresContext*
michael@0 301 ImageLoader::GetPresContext()
michael@0 302 {
michael@0 303 if (!mDocument) {
michael@0 304 return nullptr;
michael@0 305 }
michael@0 306
michael@0 307 nsIPresShell* shell = mDocument->GetShell();
michael@0 308 if (!shell) {
michael@0 309 return nullptr;
michael@0 310 }
michael@0 311
michael@0 312 return shell->GetPresContext();
michael@0 313 }
michael@0 314
michael@0 315 void InvalidateImagesCallback(nsIFrame* aFrame,
michael@0 316 FrameLayerBuilder::DisplayItemData* aItem)
michael@0 317 {
michael@0 318 nsDisplayItem::Type type = nsDisplayItem::GetDisplayItemTypeFromKey(aItem->GetDisplayItemKey());
michael@0 319 uint8_t flags = nsDisplayItem::GetDisplayItemFlagsForType(type);
michael@0 320
michael@0 321 if (flags & nsDisplayItem::TYPE_RENDERS_NO_IMAGES) {
michael@0 322 return;
michael@0 323 }
michael@0 324
michael@0 325 aItem->Invalidate();
michael@0 326
michael@0 327 // Update ancestor rendering observers (-moz-element etc)
michael@0 328 nsIFrame *f = aFrame;
michael@0 329 while (f && !f->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
michael@0 330 nsSVGEffects::InvalidateDirectRenderingObservers(f);
michael@0 331 f = nsLayoutUtils::GetCrossDocParentFrame(f);
michael@0 332 }
michael@0 333 }
michael@0 334
michael@0 335 void
michael@0 336 ImageLoader::DoRedraw(FrameSet* aFrameSet)
michael@0 337 {
michael@0 338 NS_ASSERTION(aFrameSet, "Must have a frame set");
michael@0 339 NS_ASSERTION(mDocument, "Should have returned earlier!");
michael@0 340
michael@0 341 FrameSet::size_type length = aFrameSet->Length();
michael@0 342 for (FrameSet::size_type i = 0; i < length; i++) {
michael@0 343 nsIFrame* frame = aFrameSet->ElementAt(i);
michael@0 344
michael@0 345 if (frame->StyleVisibility()->IsVisible()) {
michael@0 346 if (frame->IsFrameOfType(nsIFrame::eTablePart)) {
michael@0 347 // Tables don't necessarily build border/background display items
michael@0 348 // for the individual table part frames, so IterateRetainedDataFor
michael@0 349 // might not find the right display item.
michael@0 350 frame->InvalidateFrame();
michael@0 351 } else {
michael@0 352 FrameLayerBuilder::IterateRetainedDataFor(frame, InvalidateImagesCallback);
michael@0 353 frame->SchedulePaint();
michael@0 354 }
michael@0 355 }
michael@0 356 }
michael@0 357 }
michael@0 358
michael@0 359 NS_IMPL_ADDREF(ImageLoader)
michael@0 360 NS_IMPL_RELEASE(ImageLoader)
michael@0 361
michael@0 362 NS_INTERFACE_MAP_BEGIN(ImageLoader)
michael@0 363 NS_INTERFACE_MAP_ENTRY(imgINotificationObserver)
michael@0 364 NS_INTERFACE_MAP_ENTRY(imgIOnloadBlocker)
michael@0 365 NS_INTERFACE_MAP_END
michael@0 366
michael@0 367 NS_IMETHODIMP
michael@0 368 ImageLoader::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
michael@0 369 {
michael@0 370 if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
michael@0 371 nsCOMPtr<imgIContainer> image;
michael@0 372 aRequest->GetImage(getter_AddRefs(image));
michael@0 373 return OnStartContainer(aRequest, image);
michael@0 374 }
michael@0 375
michael@0 376 if (aType == imgINotificationObserver::IS_ANIMATED) {
michael@0 377 return OnImageIsAnimated(aRequest);
michael@0 378 }
michael@0 379
michael@0 380 if (aType == imgINotificationObserver::LOAD_COMPLETE) {
michael@0 381 return OnStopFrame(aRequest);
michael@0 382 }
michael@0 383
michael@0 384 if (aType == imgINotificationObserver::FRAME_UPDATE) {
michael@0 385 return FrameChanged(aRequest);
michael@0 386 }
michael@0 387
michael@0 388 return NS_OK;
michael@0 389 }
michael@0 390
michael@0 391 nsresult
michael@0 392 ImageLoader::OnStartContainer(imgIRequest* aRequest, imgIContainer* aImage)
michael@0 393 {
michael@0 394 nsPresContext* presContext = GetPresContext();
michael@0 395 if (!presContext) {
michael@0 396 return NS_OK;
michael@0 397 }
michael@0 398
michael@0 399 aImage->SetAnimationMode(presContext->ImageAnimationMode());
michael@0 400
michael@0 401 return NS_OK;
michael@0 402 }
michael@0 403
michael@0 404 nsresult
michael@0 405 ImageLoader::OnImageIsAnimated(imgIRequest* aRequest)
michael@0 406 {
michael@0 407 if (!mDocument) {
michael@0 408 return NS_OK;
michael@0 409 }
michael@0 410
michael@0 411 FrameSet* frameSet = nullptr;
michael@0 412 if (!mRequestToFrameMap.Get(aRequest, &frameSet)) {
michael@0 413 return NS_OK;
michael@0 414 }
michael@0 415
michael@0 416 // Register with the refresh driver now that we are aware that
michael@0 417 // we are animated.
michael@0 418 nsPresContext* presContext = GetPresContext();
michael@0 419 if (presContext) {
michael@0 420 nsLayoutUtils::RegisterImageRequest(presContext,
michael@0 421 aRequest,
michael@0 422 nullptr);
michael@0 423 }
michael@0 424
michael@0 425 return NS_OK;
michael@0 426 }
michael@0 427
michael@0 428 nsresult
michael@0 429 ImageLoader::OnStopFrame(imgIRequest *aRequest)
michael@0 430 {
michael@0 431 if (!mDocument || mInClone) {
michael@0 432 return NS_OK;
michael@0 433 }
michael@0 434
michael@0 435 FrameSet* frameSet = nullptr;
michael@0 436 if (!mRequestToFrameMap.Get(aRequest, &frameSet)) {
michael@0 437 return NS_OK;
michael@0 438 }
michael@0 439
michael@0 440 NS_ASSERTION(frameSet, "This should never be null!");
michael@0 441
michael@0 442 DoRedraw(frameSet);
michael@0 443
michael@0 444 return NS_OK;
michael@0 445 }
michael@0 446
michael@0 447 nsresult
michael@0 448 ImageLoader::FrameChanged(imgIRequest *aRequest)
michael@0 449 {
michael@0 450 if (!mDocument || mInClone) {
michael@0 451 return NS_OK;
michael@0 452 }
michael@0 453
michael@0 454 FrameSet* frameSet = nullptr;
michael@0 455 if (!mRequestToFrameMap.Get(aRequest, &frameSet)) {
michael@0 456 return NS_OK;
michael@0 457 }
michael@0 458
michael@0 459 NS_ASSERTION(frameSet, "This should never be null!");
michael@0 460
michael@0 461 DoRedraw(frameSet);
michael@0 462
michael@0 463 return NS_OK;
michael@0 464 }
michael@0 465
michael@0 466 NS_IMETHODIMP
michael@0 467 ImageLoader::BlockOnload(imgIRequest* aRequest)
michael@0 468 {
michael@0 469 if (!mDocument) {
michael@0 470 return NS_OK;
michael@0 471 }
michael@0 472
michael@0 473 mDocument->BlockOnload();
michael@0 474
michael@0 475 return NS_OK;
michael@0 476 }
michael@0 477
michael@0 478 NS_IMETHODIMP
michael@0 479 ImageLoader::UnblockOnload(imgIRequest* aRequest)
michael@0 480 {
michael@0 481 if (!mDocument) {
michael@0 482 return NS_OK;
michael@0 483 }
michael@0 484
michael@0 485 mDocument->UnblockOnload(false);
michael@0 486
michael@0 487 return NS_OK;
michael@0 488 }
michael@0 489
michael@0 490 } // namespace css
michael@0 491 } // namespace mozilla

mercurial