layout/generic/nsImageFrame.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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 /* rendering object for replaced elements with bitmap image data */
michael@0 7
michael@0 8 #include "nsImageFrame.h"
michael@0 9
michael@0 10 #include "mozilla/DebugOnly.h"
michael@0 11 #include "mozilla/EventStates.h"
michael@0 12 #include "mozilla/MouseEvents.h"
michael@0 13
michael@0 14 #include "nsCOMPtr.h"
michael@0 15 #include "nsIImageLoadingContent.h"
michael@0 16 #include "nsString.h"
michael@0 17 #include "nsPrintfCString.h"
michael@0 18 #include "nsPresContext.h"
michael@0 19 #include "nsRenderingContext.h"
michael@0 20 #include "nsIPresShell.h"
michael@0 21 #include "nsGkAtoms.h"
michael@0 22 #include "nsIDocument.h"
michael@0 23 #include "nsContentUtils.h"
michael@0 24 #include "nsCSSAnonBoxes.h"
michael@0 25 #include "nsStyleContext.h"
michael@0 26 #include "nsStyleConsts.h"
michael@0 27 #include "nsStyleCoord.h"
michael@0 28 #include "nsTransform2D.h"
michael@0 29 #include "nsImageMap.h"
michael@0 30 #include "nsIIOService.h"
michael@0 31 #include "nsILoadGroup.h"
michael@0 32 #include "nsISupportsPriority.h"
michael@0 33 #include "nsNetUtil.h"
michael@0 34 #include "nsCSSRendering.h"
michael@0 35 #include "nsIDOMHTMLAnchorElement.h"
michael@0 36 #include "nsNameSpaceManager.h"
michael@0 37 #include <algorithm>
michael@0 38 #ifdef ACCESSIBILITY
michael@0 39 #include "nsAccessibilityService.h"
michael@0 40 #endif
michael@0 41 #include "nsIDOMNode.h"
michael@0 42 #include "nsLayoutUtils.h"
michael@0 43 #include "nsDisplayList.h"
michael@0 44
michael@0 45 #include "imgIContainer.h"
michael@0 46 #include "imgLoader.h"
michael@0 47 #include "imgRequestProxy.h"
michael@0 48
michael@0 49 #include "nsCSSFrameConstructor.h"
michael@0 50 #include "nsIDOMRange.h"
michael@0 51
michael@0 52 #include "nsError.h"
michael@0 53 #include "nsBidiUtils.h"
michael@0 54 #include "nsBidiPresUtils.h"
michael@0 55 #include "mozIThirdPartyUtil.h"
michael@0 56
michael@0 57 #include "gfxRect.h"
michael@0 58 #include "ImageLayers.h"
michael@0 59 #include "ImageContainer.h"
michael@0 60 #include "nsStyleSet.h"
michael@0 61 #include "nsBlockFrame.h"
michael@0 62 #include "nsStyleStructInlines.h"
michael@0 63
michael@0 64 #include "mozilla/Preferences.h"
michael@0 65
michael@0 66 #include "mozilla/dom/Link.h"
michael@0 67
michael@0 68 using namespace mozilla;
michael@0 69
michael@0 70 // sizes (pixels) for image icon, padding and border frame
michael@0 71 #define ICON_SIZE (16)
michael@0 72 #define ICON_PADDING (3)
michael@0 73 #define ALT_BORDER_WIDTH (1)
michael@0 74
michael@0 75
michael@0 76 //we must add hooks soon
michael@0 77 #define IMAGE_EDITOR_CHECK 1
michael@0 78
michael@0 79 // Default alignment value (so we can tell an unset value from a set value)
michael@0 80 #define ALIGN_UNSET uint8_t(-1)
michael@0 81
michael@0 82 using namespace mozilla::layers;
michael@0 83 using namespace mozilla::dom;
michael@0 84
michael@0 85 // static icon information
michael@0 86 nsImageFrame::IconLoad* nsImageFrame::gIconLoad = nullptr;
michael@0 87
michael@0 88 // cached IO service for loading icons
michael@0 89 nsIIOService* nsImageFrame::sIOService;
michael@0 90
michael@0 91 // test if the width and height are fixed, looking at the style data
michael@0 92 static bool HaveFixedSize(const nsStylePosition* aStylePosition)
michael@0 93 {
michael@0 94 // check the width and height values in the reflow state's style struct
michael@0 95 // - if width and height are specified as either coord or percentage, then
michael@0 96 // the size of the image frame is constrained
michael@0 97 return aStylePosition->mWidth.IsCoordPercentCalcUnit() &&
michael@0 98 aStylePosition->mHeight.IsCoordPercentCalcUnit();
michael@0 99 }
michael@0 100 // use the data in the reflow state to decide if the image has a constrained size
michael@0 101 // (i.e. width and height that are based on the containing block size and not the image size)
michael@0 102 // so we can avoid animated GIF related reflows
michael@0 103 inline bool HaveFixedSize(const nsHTMLReflowState& aReflowState)
michael@0 104 {
michael@0 105 NS_ASSERTION(aReflowState.mStylePosition, "crappy reflowState - null stylePosition");
michael@0 106 // when an image has percent css style height or width, but ComputedHeight()
michael@0 107 // or ComputedWidth() of reflow state is NS_UNCONSTRAINEDSIZE
michael@0 108 // it needs to return false to cause an incremental reflow later
michael@0 109 // if an image is inside table like bug 156731 simple testcase III,
michael@0 110 // during pass 1 reflow, ComputedWidth() is NS_UNCONSTRAINEDSIZE
michael@0 111 // in pass 2 reflow, ComputedWidth() is 0, it also needs to return false
michael@0 112 // see bug 156731
michael@0 113 const nsStyleCoord &height = aReflowState.mStylePosition->mHeight;
michael@0 114 const nsStyleCoord &width = aReflowState.mStylePosition->mWidth;
michael@0 115 return ((height.HasPercent() &&
michael@0 116 NS_UNCONSTRAINEDSIZE == aReflowState.ComputedHeight()) ||
michael@0 117 (width.HasPercent() &&
michael@0 118 (NS_UNCONSTRAINEDSIZE == aReflowState.ComputedWidth() ||
michael@0 119 0 == aReflowState.ComputedWidth())))
michael@0 120 ? false
michael@0 121 : HaveFixedSize(aReflowState.mStylePosition);
michael@0 122 }
michael@0 123
michael@0 124 nsIFrame*
michael@0 125 NS_NewImageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
michael@0 126 {
michael@0 127 return new (aPresShell) nsImageFrame(aContext);
michael@0 128 }
michael@0 129
michael@0 130 NS_IMPL_FRAMEARENA_HELPERS(nsImageFrame)
michael@0 131
michael@0 132
michael@0 133 nsImageFrame::nsImageFrame(nsStyleContext* aContext) :
michael@0 134 ImageFrameSuper(aContext),
michael@0 135 mComputedSize(0, 0),
michael@0 136 mIntrinsicRatio(0, 0),
michael@0 137 mDisplayingIcon(false),
michael@0 138 mFirstFrameComplete(false),
michael@0 139 mReflowCallbackPosted(false)
michael@0 140 {
michael@0 141 // We assume our size is not constrained and we haven't gotten an
michael@0 142 // initial reflow yet, so don't touch those flags.
michael@0 143 mIntrinsicSize.width.SetCoordValue(0);
michael@0 144 mIntrinsicSize.height.SetCoordValue(0);
michael@0 145 }
michael@0 146
michael@0 147 nsImageFrame::~nsImageFrame()
michael@0 148 {
michael@0 149 }
michael@0 150
michael@0 151 NS_QUERYFRAME_HEAD(nsImageFrame)
michael@0 152 NS_QUERYFRAME_ENTRY(nsImageFrame)
michael@0 153 NS_QUERYFRAME_TAIL_INHERITING(ImageFrameSuper)
michael@0 154
michael@0 155 #ifdef ACCESSIBILITY
michael@0 156 a11y::AccType
michael@0 157 nsImageFrame::AccessibleType()
michael@0 158 {
michael@0 159 // Don't use GetImageMap() to avoid reentrancy into accessibility.
michael@0 160 if (HasImageMap()) {
michael@0 161 return a11y::eHTMLImageMapType;
michael@0 162 }
michael@0 163
michael@0 164 return a11y::eImageType;
michael@0 165 }
michael@0 166 #endif
michael@0 167
michael@0 168 void
michael@0 169 nsImageFrame::DisconnectMap()
michael@0 170 {
michael@0 171 if (mImageMap) {
michael@0 172 mImageMap->Destroy();
michael@0 173 NS_RELEASE(mImageMap);
michael@0 174
michael@0 175 #ifdef ACCESSIBILITY
michael@0 176 nsAccessibilityService* accService = GetAccService();
michael@0 177 if (accService) {
michael@0 178 accService->RecreateAccessible(PresContext()->PresShell(), mContent);
michael@0 179 }
michael@0 180 #endif
michael@0 181 }
michael@0 182 }
michael@0 183
michael@0 184 void
michael@0 185 nsImageFrame::DestroyFrom(nsIFrame* aDestructRoot)
michael@0 186 {
michael@0 187 if (mReflowCallbackPosted) {
michael@0 188 PresContext()->PresShell()->CancelReflowCallback(this);
michael@0 189 mReflowCallbackPosted = false;
michael@0 190 }
michael@0 191
michael@0 192 // Tell our image map, if there is one, to clean up
michael@0 193 // This causes the nsImageMap to unregister itself as
michael@0 194 // a DOM listener.
michael@0 195 DisconnectMap();
michael@0 196
michael@0 197 // set the frame to null so we don't send messages to a dead object.
michael@0 198 if (mListener) {
michael@0 199 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
michael@0 200 if (imageLoader) {
michael@0 201 // Notify our image loading content that we are going away so it can
michael@0 202 // deregister with our refresh driver.
michael@0 203 imageLoader->FrameDestroyed(this);
michael@0 204
michael@0 205 imageLoader->RemoveObserver(mListener);
michael@0 206 }
michael@0 207
michael@0 208 reinterpret_cast<nsImageListener*>(mListener.get())->SetFrame(nullptr);
michael@0 209 }
michael@0 210
michael@0 211 mListener = nullptr;
michael@0 212
michael@0 213 // If we were displaying an icon, take ourselves off the list
michael@0 214 if (mDisplayingIcon)
michael@0 215 gIconLoad->RemoveIconObserver(this);
michael@0 216
michael@0 217 nsSplittableFrame::DestroyFrom(aDestructRoot);
michael@0 218 }
michael@0 219
michael@0 220
michael@0 221
michael@0 222 void
michael@0 223 nsImageFrame::Init(nsIContent* aContent,
michael@0 224 nsIFrame* aParent,
michael@0 225 nsIFrame* aPrevInFlow)
michael@0 226 {
michael@0 227 nsSplittableFrame::Init(aContent, aParent, aPrevInFlow);
michael@0 228
michael@0 229 mListener = new nsImageListener(this);
michael@0 230
michael@0 231 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(aContent);
michael@0 232 if (!imageLoader) {
michael@0 233 NS_RUNTIMEABORT("Why do we have an nsImageFrame here at all?");
michael@0 234 }
michael@0 235
michael@0 236 imageLoader->AddObserver(mListener);
michael@0 237
michael@0 238 nsPresContext *aPresContext = PresContext();
michael@0 239
michael@0 240 if (!gIconLoad)
michael@0 241 LoadIcons(aPresContext);
michael@0 242
michael@0 243 // We have a PresContext now, so we need to notify the image content node
michael@0 244 // that it can register images.
michael@0 245 imageLoader->FrameCreated(this);
michael@0 246
michael@0 247 // Give image loads associated with an image frame a small priority boost!
michael@0 248 nsCOMPtr<imgIRequest> currentRequest;
michael@0 249 imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
michael@0 250 getter_AddRefs(currentRequest));
michael@0 251 nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(currentRequest);
michael@0 252 if (p)
michael@0 253 p->AdjustPriority(-1);
michael@0 254
michael@0 255 // If we already have an image container, OnStartContainer won't be called
michael@0 256 if (currentRequest) {
michael@0 257 nsCOMPtr<imgIContainer> image;
michael@0 258 currentRequest->GetImage(getter_AddRefs(image));
michael@0 259 OnStartContainer(currentRequest, image);
michael@0 260 }
michael@0 261 }
michael@0 262
michael@0 263 bool
michael@0 264 nsImageFrame::UpdateIntrinsicSize(imgIContainer* aImage)
michael@0 265 {
michael@0 266 NS_PRECONDITION(aImage, "null image");
michael@0 267 if (!aImage)
michael@0 268 return false;
michael@0 269
michael@0 270 IntrinsicSize oldIntrinsicSize = mIntrinsicSize;
michael@0 271 mIntrinsicSize = IntrinsicSize();
michael@0 272
michael@0 273 // Set intrinsic size to match aImage's reported intrinsic width & height.
michael@0 274 nsSize intrinsicSize;
michael@0 275 if (NS_SUCCEEDED(aImage->GetIntrinsicSize(&intrinsicSize))) {
michael@0 276 // If the image has no intrinsic width, intrinsicSize.width will be -1, and
michael@0 277 // we can leave mIntrinsicSize.width at its default value of eStyleUnit_None.
michael@0 278 // Otherwise we use intrinsicSize.width. Height works the same way.
michael@0 279 if (intrinsicSize.width != -1)
michael@0 280 mIntrinsicSize.width.SetCoordValue(intrinsicSize.width);
michael@0 281 if (intrinsicSize.height != -1)
michael@0 282 mIntrinsicSize.height.SetCoordValue(intrinsicSize.height);
michael@0 283 } else {
michael@0 284 // Failure means that the image hasn't loaded enough to report a result. We
michael@0 285 // treat this case as if the image's intrinsic size was 0x0.
michael@0 286 mIntrinsicSize.width.SetCoordValue(0);
michael@0 287 mIntrinsicSize.height.SetCoordValue(0);
michael@0 288 }
michael@0 289
michael@0 290 return mIntrinsicSize != oldIntrinsicSize;
michael@0 291 }
michael@0 292
michael@0 293 bool
michael@0 294 nsImageFrame::UpdateIntrinsicRatio(imgIContainer* aImage)
michael@0 295 {
michael@0 296 NS_PRECONDITION(aImage, "null image");
michael@0 297
michael@0 298 if (!aImage)
michael@0 299 return false;
michael@0 300
michael@0 301 nsSize oldIntrinsicRatio = mIntrinsicRatio;
michael@0 302
michael@0 303 // Set intrinsic ratio to match aImage's reported intrinsic ratio.
michael@0 304 if (NS_FAILED(aImage->GetIntrinsicRatio(&mIntrinsicRatio)))
michael@0 305 mIntrinsicRatio.SizeTo(0, 0);
michael@0 306
michael@0 307 return mIntrinsicRatio != oldIntrinsicRatio;
michael@0 308 }
michael@0 309
michael@0 310 bool
michael@0 311 nsImageFrame::GetSourceToDestTransform(nsTransform2D& aTransform)
michael@0 312 {
michael@0 313 // Set the translation components.
michael@0 314 // XXXbz does this introduce rounding errors because of the cast to
michael@0 315 // float? Should we just manually add that stuff in every time
michael@0 316 // instead?
michael@0 317 nsRect innerArea = GetInnerArea();
michael@0 318 aTransform.SetToTranslate(float(innerArea.x),
michael@0 319 float(innerArea.y - GetContinuationOffset()));
michael@0 320
michael@0 321 // Set the scale factors.
michael@0 322 if (mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord &&
michael@0 323 mIntrinsicSize.width.GetCoordValue() != 0 &&
michael@0 324 mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord &&
michael@0 325 mIntrinsicSize.height.GetCoordValue() != 0 &&
michael@0 326 mIntrinsicSize.width.GetCoordValue() != mComputedSize.width &&
michael@0 327 mIntrinsicSize.height.GetCoordValue() != mComputedSize.height) {
michael@0 328
michael@0 329 aTransform.SetScale(float(mComputedSize.width) /
michael@0 330 float(mIntrinsicSize.width.GetCoordValue()),
michael@0 331 float(mComputedSize.height) /
michael@0 332 float(mIntrinsicSize.height.GetCoordValue()));
michael@0 333 return true;
michael@0 334 }
michael@0 335
michael@0 336 return false;
michael@0 337 }
michael@0 338
michael@0 339 /*
michael@0 340 * These two functions basically do the same check. The first one
michael@0 341 * checks that the given request is the current request for our
michael@0 342 * mContent. The second checks that the given image container the
michael@0 343 * same as the image container on the current request for our
michael@0 344 * mContent.
michael@0 345 */
michael@0 346 bool
michael@0 347 nsImageFrame::IsPendingLoad(imgIRequest* aRequest) const
michael@0 348 {
michael@0 349 // Default to pending load in case of errors
michael@0 350 nsCOMPtr<nsIImageLoadingContent> imageLoader(do_QueryInterface(mContent));
michael@0 351 NS_ASSERTION(imageLoader, "No image loading content?");
michael@0 352
michael@0 353 int32_t requestType = nsIImageLoadingContent::UNKNOWN_REQUEST;
michael@0 354 imageLoader->GetRequestType(aRequest, &requestType);
michael@0 355
michael@0 356 return requestType != nsIImageLoadingContent::CURRENT_REQUEST;
michael@0 357 }
michael@0 358
michael@0 359 bool
michael@0 360 nsImageFrame::IsPendingLoad(imgIContainer* aContainer) const
michael@0 361 {
michael@0 362 // default to pending load in case of errors
michael@0 363 if (!aContainer) {
michael@0 364 NS_ERROR("No image container!");
michael@0 365 return true;
michael@0 366 }
michael@0 367
michael@0 368 nsCOMPtr<nsIImageLoadingContent> imageLoader(do_QueryInterface(mContent));
michael@0 369 NS_ASSERTION(imageLoader, "No image loading content?");
michael@0 370
michael@0 371 nsCOMPtr<imgIRequest> currentRequest;
michael@0 372 imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
michael@0 373 getter_AddRefs(currentRequest));
michael@0 374 if (!currentRequest) {
michael@0 375 NS_ERROR("No current request");
michael@0 376 return true;
michael@0 377 }
michael@0 378
michael@0 379 nsCOMPtr<imgIContainer> currentContainer;
michael@0 380 currentRequest->GetImage(getter_AddRefs(currentContainer));
michael@0 381
michael@0 382 return currentContainer != aContainer;
michael@0 383
michael@0 384 }
michael@0 385
michael@0 386 nsRect
michael@0 387 nsImageFrame::SourceRectToDest(const nsIntRect& aRect)
michael@0 388 {
michael@0 389 // When scaling the image, row N of the source image may (depending on
michael@0 390 // the scaling function) be used to draw any row in the destination image
michael@0 391 // between floor(F * (N-1)) and ceil(F * (N+1)), where F is the
michael@0 392 // floating-point scaling factor. The same holds true for columns.
michael@0 393 // So, we start by computing that bound without the floor and ceiling.
michael@0 394
michael@0 395 nsRect r(nsPresContext::CSSPixelsToAppUnits(aRect.x - 1),
michael@0 396 nsPresContext::CSSPixelsToAppUnits(aRect.y - 1),
michael@0 397 nsPresContext::CSSPixelsToAppUnits(aRect.width + 2),
michael@0 398 nsPresContext::CSSPixelsToAppUnits(aRect.height + 2));
michael@0 399
michael@0 400 nsTransform2D sourceToDest;
michael@0 401 if (!GetSourceToDestTransform(sourceToDest)) {
michael@0 402 // Failed to generate transform matrix. Return our whole inner area,
michael@0 403 // to be on the safe side (since this method is used for generating
michael@0 404 // invalidation rects).
michael@0 405 return GetInnerArea();
michael@0 406 }
michael@0 407
michael@0 408 sourceToDest.TransformCoord(&r.x, &r.y, &r.width, &r.height);
michael@0 409
michael@0 410 // Now, round the edges out to the pixel boundary.
michael@0 411 nscoord scale = nsPresContext::CSSPixelsToAppUnits(1);
michael@0 412 nscoord right = r.x + r.width;
michael@0 413 nscoord bottom = r.y + r.height;
michael@0 414
michael@0 415 r.x -= (scale + (r.x % scale)) % scale;
michael@0 416 r.y -= (scale + (r.y % scale)) % scale;
michael@0 417 r.width = right + ((scale - (right % scale)) % scale) - r.x;
michael@0 418 r.height = bottom + ((scale - (bottom % scale)) % scale) - r.y;
michael@0 419
michael@0 420 return r;
michael@0 421 }
michael@0 422
michael@0 423 // Note that we treat NS_EVENT_STATE_SUPPRESSED images as "OK". This means
michael@0 424 // that we'll construct image frames for them as needed if their display is
michael@0 425 // toggled from "none" (though we won't paint them, unless their visibility
michael@0 426 // is changed too).
michael@0 427 #define BAD_STATES (NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_USERDISABLED | \
michael@0 428 NS_EVENT_STATE_LOADING)
michael@0 429
michael@0 430 // This is a macro so that we don't evaluate the boolean last arg
michael@0 431 // unless we have to; it can be expensive
michael@0 432 #define IMAGE_OK(_state, _loadingOK) \
michael@0 433 (!(_state).HasAtLeastOneOfStates(BAD_STATES) || \
michael@0 434 (!(_state).HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_USERDISABLED) && \
michael@0 435 (_state).HasState(NS_EVENT_STATE_LOADING) && (_loadingOK)))
michael@0 436
michael@0 437 /* static */
michael@0 438 bool
michael@0 439 nsImageFrame::ShouldCreateImageFrameFor(Element* aElement,
michael@0 440 nsStyleContext* aStyleContext)
michael@0 441 {
michael@0 442 EventStates state = aElement->State();
michael@0 443 if (IMAGE_OK(state,
michael@0 444 HaveFixedSize(aStyleContext->StylePosition()))) {
michael@0 445 // Image is fine; do the image frame thing
michael@0 446 return true;
michael@0 447 }
michael@0 448
michael@0 449 // Check if we want to use a placeholder box with an icon or just
michael@0 450 // let the presShell make us into inline text. Decide as follows:
michael@0 451 //
michael@0 452 // - if our special "force icons" style is set, show an icon
michael@0 453 // - else if our "do not show placeholders" pref is set, skip the icon
michael@0 454 // - else:
michael@0 455 // - if there is a src attribute, there is no alt attribute,
michael@0 456 // and this is not an <object> (which could not possibly have
michael@0 457 // such an attribute), show an icon.
michael@0 458 // - if QuirksMode, and the IMG has a size show an icon.
michael@0 459 // - otherwise, skip the icon
michael@0 460 bool useSizedBox;
michael@0 461
michael@0 462 if (aStyleContext->StyleUIReset()->mForceBrokenImageIcon) {
michael@0 463 useSizedBox = true;
michael@0 464 }
michael@0 465 else if (gIconLoad && gIconLoad->mPrefForceInlineAltText) {
michael@0 466 useSizedBox = false;
michael@0 467 }
michael@0 468 else if (aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::src) &&
michael@0 469 !aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::alt) &&
michael@0 470 !aElement->IsHTML(nsGkAtoms::object) &&
michael@0 471 !aElement->IsHTML(nsGkAtoms::input)) {
michael@0 472 // Use a sized box if we have no alt text. This means no alt attribute
michael@0 473 // and the node is not an object or an input (since those always have alt
michael@0 474 // text).
michael@0 475 useSizedBox = true;
michael@0 476 }
michael@0 477 else if (aStyleContext->PresContext()->CompatibilityMode() !=
michael@0 478 eCompatibility_NavQuirks) {
michael@0 479 useSizedBox = false;
michael@0 480 }
michael@0 481 else {
michael@0 482 // check whether we have fixed size
michael@0 483 useSizedBox = HaveFixedSize(aStyleContext->StylePosition());
michael@0 484 }
michael@0 485
michael@0 486 return useSizedBox;
michael@0 487 }
michael@0 488
michael@0 489 nsresult
michael@0 490 nsImageFrame::Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aData)
michael@0 491 {
michael@0 492 if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
michael@0 493 nsCOMPtr<imgIContainer> image;
michael@0 494 aRequest->GetImage(getter_AddRefs(image));
michael@0 495 return OnStartContainer(aRequest, image);
michael@0 496 }
michael@0 497
michael@0 498 if (aType == imgINotificationObserver::FRAME_UPDATE) {
michael@0 499 return OnDataAvailable(aRequest, aData);
michael@0 500 }
michael@0 501
michael@0 502 if (aType == imgINotificationObserver::FRAME_COMPLETE) {
michael@0 503 mFirstFrameComplete = true;
michael@0 504 }
michael@0 505
michael@0 506 if (aType == imgINotificationObserver::LOAD_COMPLETE) {
michael@0 507 uint32_t imgStatus;
michael@0 508 aRequest->GetImageStatus(&imgStatus);
michael@0 509 nsresult status =
michael@0 510 imgStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK;
michael@0 511 return OnStopRequest(aRequest, status);
michael@0 512 }
michael@0 513
michael@0 514 return NS_OK;
michael@0 515 }
michael@0 516
michael@0 517 static bool
michael@0 518 SizeIsAvailable(imgIRequest* aRequest)
michael@0 519 {
michael@0 520 if (!aRequest)
michael@0 521 return false;
michael@0 522
michael@0 523 uint32_t imageStatus = 0;
michael@0 524 nsresult rv = aRequest->GetImageStatus(&imageStatus);
michael@0 525
michael@0 526 return NS_SUCCEEDED(rv) && (imageStatus & imgIRequest::STATUS_SIZE_AVAILABLE);
michael@0 527 }
michael@0 528
michael@0 529 nsresult
michael@0 530 nsImageFrame::OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage)
michael@0 531 {
michael@0 532 if (!aImage) return NS_ERROR_INVALID_ARG;
michael@0 533
michael@0 534 /* Get requested animation policy from the pres context:
michael@0 535 * normal = 0
michael@0 536 * one frame = 1
michael@0 537 * one loop = 2
michael@0 538 */
michael@0 539 nsPresContext *presContext = PresContext();
michael@0 540 aImage->SetAnimationMode(presContext->ImageAnimationMode());
michael@0 541
michael@0 542 if (IsPendingLoad(aRequest)) {
michael@0 543 // We don't care
michael@0 544 return NS_OK;
michael@0 545 }
michael@0 546
michael@0 547 bool intrinsicSizeChanged = false;
michael@0 548 if (SizeIsAvailable(aRequest)) {
michael@0 549 // This is valid and for the current request, so update our stored image
michael@0 550 // container, orienting according to our style.
michael@0 551 mImage = nsLayoutUtils::OrientImage(aImage, StyleVisibility()->mImageOrientation);
michael@0 552
michael@0 553 intrinsicSizeChanged = UpdateIntrinsicSize(mImage);
michael@0 554 intrinsicSizeChanged = UpdateIntrinsicRatio(mImage) || intrinsicSizeChanged;
michael@0 555 } else {
michael@0 556 // We no longer have a valid image, so release our stored image container.
michael@0 557 mImage = nullptr;
michael@0 558
michael@0 559 // Have to size to 0,0 so that GetDesiredSize recalculates the size.
michael@0 560 mIntrinsicSize.width.SetCoordValue(0);
michael@0 561 mIntrinsicSize.height.SetCoordValue(0);
michael@0 562 mIntrinsicRatio.SizeTo(0, 0);
michael@0 563 intrinsicSizeChanged = true;
michael@0 564 }
michael@0 565
michael@0 566 if (intrinsicSizeChanged && (mState & IMAGE_GOTINITIALREFLOW)) {
michael@0 567 // Now we need to reflow if we have an unconstrained size and have
michael@0 568 // already gotten the initial reflow
michael@0 569 if (!(mState & IMAGE_SIZECONSTRAINED)) {
michael@0 570 nsIPresShell *presShell = presContext->GetPresShell();
michael@0 571 NS_ASSERTION(presShell, "No PresShell.");
michael@0 572 if (presShell) {
michael@0 573 presShell->FrameNeedsReflow(this, nsIPresShell::eStyleChange,
michael@0 574 NS_FRAME_IS_DIRTY);
michael@0 575 }
michael@0 576 }
michael@0 577 }
michael@0 578
michael@0 579 return NS_OK;
michael@0 580 }
michael@0 581
michael@0 582 nsresult
michael@0 583 nsImageFrame::OnDataAvailable(imgIRequest *aRequest,
michael@0 584 const nsIntRect *aRect)
michael@0 585 {
michael@0 586 if (mFirstFrameComplete) {
michael@0 587 nsCOMPtr<imgIContainer> container;
michael@0 588 aRequest->GetImage(getter_AddRefs(container));
michael@0 589 return FrameChanged(aRequest, container);
michael@0 590 }
michael@0 591
michael@0 592 // XXX do we need to make sure that the reflow from the
michael@0 593 // OnStartContainer has been processed before we start calling
michael@0 594 // invalidate?
michael@0 595
michael@0 596 NS_ENSURE_ARG_POINTER(aRect);
michael@0 597
michael@0 598 if (!(mState & IMAGE_GOTINITIALREFLOW)) {
michael@0 599 // Don't bother to do anything; we have a reflow coming up!
michael@0 600 return NS_OK;
michael@0 601 }
michael@0 602
michael@0 603 if (IsPendingLoad(aRequest)) {
michael@0 604 // We don't care
michael@0 605 return NS_OK;
michael@0 606 }
michael@0 607
michael@0 608 #ifdef DEBUG_decode
michael@0 609 printf("Source rect (%d,%d,%d,%d)\n",
michael@0 610 aRect->x, aRect->y, aRect->width, aRect->height);
michael@0 611 #endif
michael@0 612
michael@0 613 if (aRect->IsEqualInterior(nsIntRect::GetMaxSizedIntRect())) {
michael@0 614 InvalidateFrame(nsDisplayItem::TYPE_IMAGE);
michael@0 615 InvalidateFrame(nsDisplayItem::TYPE_ALT_FEEDBACK);
michael@0 616 } else {
michael@0 617 nsRect invalid = SourceRectToDest(*aRect);
michael@0 618 InvalidateFrameWithRect(invalid, nsDisplayItem::TYPE_IMAGE);
michael@0 619 InvalidateFrameWithRect(invalid, nsDisplayItem::TYPE_ALT_FEEDBACK);
michael@0 620 }
michael@0 621
michael@0 622 return NS_OK;
michael@0 623 }
michael@0 624
michael@0 625 nsresult
michael@0 626 nsImageFrame::OnStopRequest(imgIRequest *aRequest,
michael@0 627 nsresult aStatus)
michael@0 628 {
michael@0 629 // Check what request type we're dealing with
michael@0 630 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
michael@0 631 NS_ASSERTION(imageLoader, "Who's notifying us??");
michael@0 632 int32_t loadType = nsIImageLoadingContent::UNKNOWN_REQUEST;
michael@0 633 imageLoader->GetRequestType(aRequest, &loadType);
michael@0 634 if (loadType != nsIImageLoadingContent::CURRENT_REQUEST &&
michael@0 635 loadType != nsIImageLoadingContent::PENDING_REQUEST) {
michael@0 636 return NS_ERROR_FAILURE;
michael@0 637 }
michael@0 638
michael@0 639 NotifyNewCurrentRequest(aRequest, aStatus);
michael@0 640 return NS_OK;
michael@0 641 }
michael@0 642
michael@0 643 void
michael@0 644 nsImageFrame::NotifyNewCurrentRequest(imgIRequest *aRequest,
michael@0 645 nsresult aStatus)
michael@0 646 {
michael@0 647 nsCOMPtr<imgIContainer> image;
michael@0 648 aRequest->GetImage(getter_AddRefs(image));
michael@0 649 NS_ASSERTION(image || NS_FAILED(aStatus), "Successful load with no container?");
michael@0 650
michael@0 651 // May have to switch sizes here!
michael@0 652 bool intrinsicSizeChanged = true;
michael@0 653 if (NS_SUCCEEDED(aStatus) && image && SizeIsAvailable(aRequest)) {
michael@0 654 // Update our stored image container, orienting according to our style.
michael@0 655 mImage = nsLayoutUtils::OrientImage(image, StyleVisibility()->mImageOrientation);
michael@0 656
michael@0 657 intrinsicSizeChanged = UpdateIntrinsicSize(mImage);
michael@0 658 intrinsicSizeChanged = UpdateIntrinsicRatio(mImage) || intrinsicSizeChanged;
michael@0 659 } else {
michael@0 660 // We no longer have a valid image, so release our stored image container.
michael@0 661 mImage = nullptr;
michael@0 662
michael@0 663 // Have to size to 0,0 so that GetDesiredSize recalculates the size
michael@0 664 mIntrinsicSize.width.SetCoordValue(0);
michael@0 665 mIntrinsicSize.height.SetCoordValue(0);
michael@0 666 mIntrinsicRatio.SizeTo(0, 0);
michael@0 667 }
michael@0 668
michael@0 669 if (mState & IMAGE_GOTINITIALREFLOW) { // do nothing if we haven't gotten the initial reflow yet
michael@0 670 if (!(mState & IMAGE_SIZECONSTRAINED) && intrinsicSizeChanged) {
michael@0 671 nsIPresShell *presShell = PresContext()->GetPresShell();
michael@0 672 if (presShell) {
michael@0 673 presShell->FrameNeedsReflow(this, nsIPresShell::eStyleChange,
michael@0 674 NS_FRAME_IS_DIRTY);
michael@0 675 }
michael@0 676 }
michael@0 677 // Update border+content to account for image change
michael@0 678 InvalidateFrame();
michael@0 679 }
michael@0 680 }
michael@0 681
michael@0 682 nsresult
michael@0 683 nsImageFrame::FrameChanged(imgIRequest *aRequest,
michael@0 684 imgIContainer *aContainer)
michael@0 685 {
michael@0 686 if (!StyleVisibility()->IsVisible()) {
michael@0 687 return NS_OK;
michael@0 688 }
michael@0 689
michael@0 690 if (IsPendingLoad(aContainer)) {
michael@0 691 // We don't care about it
michael@0 692 return NS_OK;
michael@0 693 }
michael@0 694
michael@0 695 InvalidateLayer(nsDisplayItem::TYPE_IMAGE);
michael@0 696 return NS_OK;
michael@0 697 }
michael@0 698
michael@0 699 void
michael@0 700 nsImageFrame::EnsureIntrinsicSizeAndRatio(nsPresContext* aPresContext)
michael@0 701 {
michael@0 702 // If mIntrinsicSize.width and height are 0, then we need to update from the
michael@0 703 // image container.
michael@0 704 if (mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord &&
michael@0 705 mIntrinsicSize.width.GetCoordValue() == 0 &&
michael@0 706 mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord &&
michael@0 707 mIntrinsicSize.height.GetCoordValue() == 0) {
michael@0 708
michael@0 709 if (mImage) {
michael@0 710 UpdateIntrinsicSize(mImage);
michael@0 711 UpdateIntrinsicRatio(mImage);
michael@0 712 } else {
michael@0 713 // image request is null or image size not known, probably an
michael@0 714 // invalid image specified
michael@0 715 // - make the image big enough for the icon (it may not be
michael@0 716 // used if inline alt expansion is used instead)
michael@0 717 if (!(GetStateBits() & NS_FRAME_GENERATED_CONTENT)) {
michael@0 718 nscoord edgeLengthToUse =
michael@0 719 nsPresContext::CSSPixelsToAppUnits(
michael@0 720 ICON_SIZE + (2 * (ICON_PADDING + ALT_BORDER_WIDTH)));
michael@0 721 mIntrinsicSize.width.SetCoordValue(edgeLengthToUse);
michael@0 722 mIntrinsicSize.height.SetCoordValue(edgeLengthToUse);
michael@0 723 mIntrinsicRatio.SizeTo(1, 1);
michael@0 724 }
michael@0 725 }
michael@0 726 }
michael@0 727 }
michael@0 728
michael@0 729 /* virtual */ nsSize
michael@0 730 nsImageFrame::ComputeSize(nsRenderingContext *aRenderingContext,
michael@0 731 nsSize aCBSize, nscoord aAvailableWidth,
michael@0 732 nsSize aMargin, nsSize aBorder, nsSize aPadding,
michael@0 733 uint32_t aFlags)
michael@0 734 {
michael@0 735 nsPresContext *presContext = PresContext();
michael@0 736 EnsureIntrinsicSizeAndRatio(presContext);
michael@0 737
michael@0 738 return nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(
michael@0 739 aRenderingContext, this,
michael@0 740 mIntrinsicSize, mIntrinsicRatio, aCBSize,
michael@0 741 aMargin, aBorder, aPadding);
michael@0 742 }
michael@0 743
michael@0 744 nsRect
michael@0 745 nsImageFrame::GetInnerArea() const
michael@0 746 {
michael@0 747 return GetContentRect() - GetPosition();
michael@0 748 }
michael@0 749
michael@0 750 // get the offset into the content area of the image where aImg starts if it is a continuation.
michael@0 751 nscoord
michael@0 752 nsImageFrame::GetContinuationOffset() const
michael@0 753 {
michael@0 754 nscoord offset = 0;
michael@0 755 for (nsIFrame *f = GetPrevInFlow(); f; f = f->GetPrevInFlow()) {
michael@0 756 offset += f->GetContentRect().height;
michael@0 757 }
michael@0 758 NS_ASSERTION(offset >= 0, "bogus GetContentRect");
michael@0 759 return offset;
michael@0 760 }
michael@0 761
michael@0 762 /* virtual */ nscoord
michael@0 763 nsImageFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
michael@0 764 {
michael@0 765 // XXX The caller doesn't account for constraints of the height,
michael@0 766 // min-height, and max-height properties.
michael@0 767 DebugOnly<nscoord> result;
michael@0 768 DISPLAY_MIN_WIDTH(this, result);
michael@0 769 nsPresContext *presContext = PresContext();
michael@0 770 EnsureIntrinsicSizeAndRatio(presContext);
michael@0 771 return mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord ?
michael@0 772 mIntrinsicSize.width.GetCoordValue() : 0;
michael@0 773 }
michael@0 774
michael@0 775 /* virtual */ nscoord
michael@0 776 nsImageFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
michael@0 777 {
michael@0 778 // XXX The caller doesn't account for constraints of the height,
michael@0 779 // min-height, and max-height properties.
michael@0 780 DebugOnly<nscoord> result;
michael@0 781 DISPLAY_PREF_WIDTH(this, result);
michael@0 782 nsPresContext *presContext = PresContext();
michael@0 783 EnsureIntrinsicSizeAndRatio(presContext);
michael@0 784 // convert from normal twips to scaled twips (printing...)
michael@0 785 return mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord ?
michael@0 786 mIntrinsicSize.width.GetCoordValue() : 0;
michael@0 787 }
michael@0 788
michael@0 789 /* virtual */ IntrinsicSize
michael@0 790 nsImageFrame::GetIntrinsicSize()
michael@0 791 {
michael@0 792 return mIntrinsicSize;
michael@0 793 }
michael@0 794
michael@0 795 /* virtual */ nsSize
michael@0 796 nsImageFrame::GetIntrinsicRatio()
michael@0 797 {
michael@0 798 return mIntrinsicRatio;
michael@0 799 }
michael@0 800
michael@0 801 nsresult
michael@0 802 nsImageFrame::Reflow(nsPresContext* aPresContext,
michael@0 803 nsHTMLReflowMetrics& aMetrics,
michael@0 804 const nsHTMLReflowState& aReflowState,
michael@0 805 nsReflowStatus& aStatus)
michael@0 806 {
michael@0 807 DO_GLOBAL_REFLOW_COUNT("nsImageFrame");
michael@0 808 DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
michael@0 809 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
michael@0 810 ("enter nsImageFrame::Reflow: availSize=%d,%d",
michael@0 811 aReflowState.AvailableWidth(), aReflowState.AvailableHeight()));
michael@0 812
michael@0 813 NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow");
michael@0 814
michael@0 815 aStatus = NS_FRAME_COMPLETE;
michael@0 816
michael@0 817 // see if we have a frozen size (i.e. a fixed width and height)
michael@0 818 if (HaveFixedSize(aReflowState)) {
michael@0 819 mState |= IMAGE_SIZECONSTRAINED;
michael@0 820 } else {
michael@0 821 mState &= ~IMAGE_SIZECONSTRAINED;
michael@0 822 }
michael@0 823
michael@0 824 // XXXldb These two bits are almost exact opposites (except in the
michael@0 825 // middle of the initial reflow); remove IMAGE_GOTINITIALREFLOW.
michael@0 826 if (GetStateBits() & NS_FRAME_FIRST_REFLOW) {
michael@0 827 mState |= IMAGE_GOTINITIALREFLOW;
michael@0 828 }
michael@0 829
michael@0 830 mComputedSize =
michael@0 831 nsSize(aReflowState.ComputedWidth(), aReflowState.ComputedHeight());
michael@0 832
michael@0 833 aMetrics.Width() = mComputedSize.width;
michael@0 834 aMetrics.Height() = mComputedSize.height;
michael@0 835
michael@0 836 // add borders and padding
michael@0 837 aMetrics.Width() += aReflowState.ComputedPhysicalBorderPadding().LeftRight();
michael@0 838 aMetrics.Height() += aReflowState.ComputedPhysicalBorderPadding().TopBottom();
michael@0 839
michael@0 840 if (GetPrevInFlow()) {
michael@0 841 aMetrics.Width() = GetPrevInFlow()->GetSize().width;
michael@0 842 nscoord y = GetContinuationOffset();
michael@0 843 aMetrics.Height() -= y + aReflowState.ComputedPhysicalBorderPadding().top;
michael@0 844 aMetrics.Height() = std::max(0, aMetrics.Height());
michael@0 845 }
michael@0 846
michael@0 847
michael@0 848 // we have to split images if we are:
michael@0 849 // in Paginated mode, we need to have a constrained height, and have a height larger than our available height
michael@0 850 uint32_t loadStatus = imgIRequest::STATUS_NONE;
michael@0 851 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
michael@0 852 NS_ASSERTION(imageLoader, "No content node??");
michael@0 853 if (imageLoader) {
michael@0 854 nsCOMPtr<imgIRequest> currentRequest;
michael@0 855 imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
michael@0 856 getter_AddRefs(currentRequest));
michael@0 857 if (currentRequest) {
michael@0 858 currentRequest->GetImageStatus(&loadStatus);
michael@0 859 }
michael@0 860 }
michael@0 861 if (aPresContext->IsPaginated() &&
michael@0 862 ((loadStatus & imgIRequest::STATUS_SIZE_AVAILABLE) || (mState & IMAGE_SIZECONSTRAINED)) &&
michael@0 863 NS_UNCONSTRAINEDSIZE != aReflowState.AvailableHeight() &&
michael@0 864 aMetrics.Height() > aReflowState.AvailableHeight()) {
michael@0 865 // our desired height was greater than 0, so to avoid infinite
michael@0 866 // splitting, use 1 pixel as the min
michael@0 867 aMetrics.Height() = std::max(nsPresContext::CSSPixelsToAppUnits(1), aReflowState.AvailableHeight());
michael@0 868 aStatus = NS_FRAME_NOT_COMPLETE;
michael@0 869 }
michael@0 870
michael@0 871 aMetrics.SetOverflowAreasToDesiredBounds();
michael@0 872 EventStates contentState = mContent->AsElement()->State();
michael@0 873 bool imageOK = IMAGE_OK(contentState, true);
michael@0 874
michael@0 875 // Determine if the size is available
michael@0 876 bool haveSize = false;
michael@0 877 if (loadStatus & imgIRequest::STATUS_SIZE_AVAILABLE) {
michael@0 878 haveSize = true;
michael@0 879 }
michael@0 880
michael@0 881 if (!imageOK || !haveSize) {
michael@0 882 nsRect altFeedbackSize(0, 0,
michael@0 883 nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+2*(ICON_PADDING+ALT_BORDER_WIDTH)),
michael@0 884 nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+2*(ICON_PADDING+ALT_BORDER_WIDTH)));
michael@0 885 // We include the altFeedbackSize in our visual overflow, but not in our
michael@0 886 // scrollable overflow, since it doesn't really need to be scrolled to
michael@0 887 // outside the image.
michael@0 888 static_assert(eOverflowType_LENGTH == 2, "Unknown overflow types?");
michael@0 889 nsRect& visualOverflow = aMetrics.VisualOverflow();
michael@0 890 visualOverflow.UnionRect(visualOverflow, altFeedbackSize);
michael@0 891 }
michael@0 892 FinishAndStoreOverflow(&aMetrics);
michael@0 893
michael@0 894 if ((GetStateBits() & NS_FRAME_FIRST_REFLOW) && !mReflowCallbackPosted) {
michael@0 895 nsIPresShell* shell = PresContext()->PresShell();
michael@0 896 mReflowCallbackPosted = true;
michael@0 897 shell->PostReflowCallback(this);
michael@0 898 }
michael@0 899
michael@0 900 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
michael@0 901 ("exit nsImageFrame::Reflow: size=%d,%d",
michael@0 902 aMetrics.Width(), aMetrics.Height()));
michael@0 903 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics);
michael@0 904 return NS_OK;
michael@0 905 }
michael@0 906
michael@0 907 bool
michael@0 908 nsImageFrame::ReflowFinished()
michael@0 909 {
michael@0 910 mReflowCallbackPosted = false;
michael@0 911
michael@0 912 nsLayoutUtils::UpdateImageVisibilityForFrame(this);
michael@0 913
michael@0 914 return false;
michael@0 915 }
michael@0 916
michael@0 917 void
michael@0 918 nsImageFrame::ReflowCallbackCanceled()
michael@0 919 {
michael@0 920 mReflowCallbackPosted = false;
michael@0 921 }
michael@0 922
michael@0 923 // Computes the width of the specified string. aMaxWidth specifies the maximum
michael@0 924 // width available. Once this limit is reached no more characters are measured.
michael@0 925 // The number of characters that fit within the maximum width are returned in
michael@0 926 // aMaxFit. NOTE: it is assumed that the fontmetrics have already been selected
michael@0 927 // into the rendering context before this is called (for performance). MMP
michael@0 928 nscoord
michael@0 929 nsImageFrame::MeasureString(const char16_t* aString,
michael@0 930 int32_t aLength,
michael@0 931 nscoord aMaxWidth,
michael@0 932 uint32_t& aMaxFit,
michael@0 933 nsRenderingContext& aContext)
michael@0 934 {
michael@0 935 nscoord totalWidth = 0;
michael@0 936 aContext.SetTextRunRTL(false);
michael@0 937 nscoord spaceWidth = aContext.GetWidth(' ');
michael@0 938
michael@0 939 aMaxFit = 0;
michael@0 940 while (aLength > 0) {
michael@0 941 // Find the next place we can line break
michael@0 942 uint32_t len = aLength;
michael@0 943 bool trailingSpace = false;
michael@0 944 for (int32_t i = 0; i < aLength; i++) {
michael@0 945 if (dom::IsSpaceCharacter(aString[i]) && (i > 0)) {
michael@0 946 len = i; // don't include the space when measuring
michael@0 947 trailingSpace = true;
michael@0 948 break;
michael@0 949 }
michael@0 950 }
michael@0 951
michael@0 952 // Measure this chunk of text, and see if it fits
michael@0 953 nscoord width =
michael@0 954 nsLayoutUtils::GetStringWidth(this, &aContext, aString, len);
michael@0 955 bool fits = (totalWidth + width) <= aMaxWidth;
michael@0 956
michael@0 957 // If it fits on the line, or it's the first word we've processed then
michael@0 958 // include it
michael@0 959 if (fits || (0 == totalWidth)) {
michael@0 960 // New piece fits
michael@0 961 totalWidth += width;
michael@0 962
michael@0 963 // If there's a trailing space then see if it fits as well
michael@0 964 if (trailingSpace) {
michael@0 965 if ((totalWidth + spaceWidth) <= aMaxWidth) {
michael@0 966 totalWidth += spaceWidth;
michael@0 967 } else {
michael@0 968 // Space won't fit. Leave it at the end but don't include it in
michael@0 969 // the width
michael@0 970 fits = false;
michael@0 971 }
michael@0 972
michael@0 973 len++;
michael@0 974 }
michael@0 975
michael@0 976 aMaxFit += len;
michael@0 977 aString += len;
michael@0 978 aLength -= len;
michael@0 979 }
michael@0 980
michael@0 981 if (!fits) {
michael@0 982 break;
michael@0 983 }
michael@0 984 }
michael@0 985 return totalWidth;
michael@0 986 }
michael@0 987
michael@0 988 // Formats the alt-text to fit within the specified rectangle. Breaks lines
michael@0 989 // between words if a word would extend past the edge of the rectangle
michael@0 990 void
michael@0 991 nsImageFrame::DisplayAltText(nsPresContext* aPresContext,
michael@0 992 nsRenderingContext& aRenderingContext,
michael@0 993 const nsString& aAltText,
michael@0 994 const nsRect& aRect)
michael@0 995 {
michael@0 996 // Set font and color
michael@0 997 aRenderingContext.SetColor(StyleColor()->mColor);
michael@0 998 nsRefPtr<nsFontMetrics> fm;
michael@0 999 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm),
michael@0 1000 nsLayoutUtils::FontSizeInflationFor(this));
michael@0 1001 aRenderingContext.SetFont(fm);
michael@0 1002
michael@0 1003 // Format the text to display within the formatting rect
michael@0 1004
michael@0 1005 nscoord maxAscent = fm->MaxAscent();
michael@0 1006 nscoord maxDescent = fm->MaxDescent();
michael@0 1007 nscoord height = fm->MaxHeight();
michael@0 1008
michael@0 1009 // XXX It would be nice if there was a way to have the font metrics tell
michael@0 1010 // use where to break the text given a maximum width. At a minimum we need
michael@0 1011 // to be able to get the break character...
michael@0 1012 const char16_t* str = aAltText.get();
michael@0 1013 int32_t strLen = aAltText.Length();
michael@0 1014 nscoord y = aRect.y;
michael@0 1015
michael@0 1016 if (!aPresContext->BidiEnabled() && HasRTLChars(aAltText)) {
michael@0 1017 aPresContext->SetBidiEnabled();
michael@0 1018 }
michael@0 1019
michael@0 1020 // Always show the first line, even if we have to clip it below
michael@0 1021 bool firstLine = true;
michael@0 1022 while ((strLen > 0) && (firstLine || (y + maxDescent) < aRect.YMost())) {
michael@0 1023 // Determine how much of the text to display on this line
michael@0 1024 uint32_t maxFit; // number of characters that fit
michael@0 1025 nscoord strWidth = MeasureString(str, strLen, aRect.width, maxFit,
michael@0 1026 aRenderingContext);
michael@0 1027
michael@0 1028 // Display the text
michael@0 1029 nsresult rv = NS_ERROR_FAILURE;
michael@0 1030
michael@0 1031 if (aPresContext->BidiEnabled()) {
michael@0 1032 const nsStyleVisibility* vis = StyleVisibility();
michael@0 1033 if (vis->mDirection == NS_STYLE_DIRECTION_RTL)
michael@0 1034 rv = nsBidiPresUtils::RenderText(str, maxFit, NSBIDI_RTL,
michael@0 1035 aPresContext, aRenderingContext,
michael@0 1036 aRenderingContext,
michael@0 1037 aRect.XMost() - strWidth, y + maxAscent);
michael@0 1038 else
michael@0 1039 rv = nsBidiPresUtils::RenderText(str, maxFit, NSBIDI_LTR,
michael@0 1040 aPresContext, aRenderingContext,
michael@0 1041 aRenderingContext,
michael@0 1042 aRect.x, y + maxAscent);
michael@0 1043 }
michael@0 1044 if (NS_FAILED(rv))
michael@0 1045 aRenderingContext.DrawString(str, maxFit, aRect.x, y + maxAscent);
michael@0 1046
michael@0 1047 // Move to the next line
michael@0 1048 str += maxFit;
michael@0 1049 strLen -= maxFit;
michael@0 1050 y += height;
michael@0 1051 firstLine = false;
michael@0 1052 }
michael@0 1053 }
michael@0 1054
michael@0 1055 struct nsRecessedBorder : public nsStyleBorder {
michael@0 1056 nsRecessedBorder(nscoord aBorderWidth, nsPresContext* aPresContext)
michael@0 1057 : nsStyleBorder(aPresContext)
michael@0 1058 {
michael@0 1059 NS_FOR_CSS_SIDES(side) {
michael@0 1060 // Note: use SetBorderColor here because we want to make sure
michael@0 1061 // the "special" flags are unset.
michael@0 1062 SetBorderColor(side, NS_RGB(0, 0, 0));
michael@0 1063 mBorder.Side(side) = aBorderWidth;
michael@0 1064 // Note: use SetBorderStyle here because we want to affect
michael@0 1065 // mComputedBorder
michael@0 1066 SetBorderStyle(side, NS_STYLE_BORDER_STYLE_INSET);
michael@0 1067 }
michael@0 1068 }
michael@0 1069 };
michael@0 1070
michael@0 1071 class nsDisplayAltFeedback : public nsDisplayItem {
michael@0 1072 public:
michael@0 1073 nsDisplayAltFeedback(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
michael@0 1074 : nsDisplayItem(aBuilder, aFrame) {}
michael@0 1075
michael@0 1076 virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
michael@0 1077 bool* aSnap) MOZ_OVERRIDE
michael@0 1078 {
michael@0 1079 *aSnap = false;
michael@0 1080 return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
michael@0 1081 }
michael@0 1082
michael@0 1083 virtual void Paint(nsDisplayListBuilder* aBuilder,
michael@0 1084 nsRenderingContext* aCtx) MOZ_OVERRIDE
michael@0 1085 {
michael@0 1086 nsImageFrame* f = static_cast<nsImageFrame*>(mFrame);
michael@0 1087 EventStates state = f->GetContent()->AsElement()->State();
michael@0 1088 f->DisplayAltFeedback(*aCtx,
michael@0 1089 mVisibleRect,
michael@0 1090 IMAGE_OK(state, true)
michael@0 1091 ? nsImageFrame::gIconLoad->mLoadingImage
michael@0 1092 : nsImageFrame::gIconLoad->mBrokenImage,
michael@0 1093 ToReferenceFrame());
michael@0 1094
michael@0 1095 }
michael@0 1096
michael@0 1097 NS_DISPLAY_DECL_NAME("AltFeedback", TYPE_ALT_FEEDBACK)
michael@0 1098 };
michael@0 1099
michael@0 1100 void
michael@0 1101 nsImageFrame::DisplayAltFeedback(nsRenderingContext& aRenderingContext,
michael@0 1102 const nsRect& aDirtyRect,
michael@0 1103 imgIRequest* aRequest,
michael@0 1104 nsPoint aPt)
michael@0 1105 {
michael@0 1106 // We should definitely have a gIconLoad here.
michael@0 1107 NS_ABORT_IF_FALSE(gIconLoad, "How did we succeed in Init then?");
michael@0 1108
michael@0 1109 // Calculate the inner area
michael@0 1110 nsRect inner = GetInnerArea() + aPt;
michael@0 1111
michael@0 1112 // Display a recessed one pixel border
michael@0 1113 nscoord borderEdgeWidth = nsPresContext::CSSPixelsToAppUnits(ALT_BORDER_WIDTH);
michael@0 1114
michael@0 1115 // if inner area is empty, then make it big enough for at least the icon
michael@0 1116 if (inner.IsEmpty()){
michael@0 1117 inner.SizeTo(2*(nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+ICON_PADDING+ALT_BORDER_WIDTH)),
michael@0 1118 2*(nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+ICON_PADDING+ALT_BORDER_WIDTH)));
michael@0 1119 }
michael@0 1120
michael@0 1121 // Make sure we have enough room to actually render the border within
michael@0 1122 // our frame bounds
michael@0 1123 if ((inner.width < 2 * borderEdgeWidth) || (inner.height < 2 * borderEdgeWidth)) {
michael@0 1124 return;
michael@0 1125 }
michael@0 1126
michael@0 1127 // Paint the border
michael@0 1128 nsRecessedBorder recessedBorder(borderEdgeWidth, PresContext());
michael@0 1129 nsCSSRendering::PaintBorderWithStyleBorder(PresContext(), aRenderingContext,
michael@0 1130 this, inner, inner,
michael@0 1131 recessedBorder, mStyleContext);
michael@0 1132
michael@0 1133 // Adjust the inner rect to account for the one pixel recessed border,
michael@0 1134 // and a six pixel padding on each edge
michael@0 1135 inner.Deflate(nsPresContext::CSSPixelsToAppUnits(ICON_PADDING+ALT_BORDER_WIDTH),
michael@0 1136 nsPresContext::CSSPixelsToAppUnits(ICON_PADDING+ALT_BORDER_WIDTH));
michael@0 1137 if (inner.IsEmpty()) {
michael@0 1138 return;
michael@0 1139 }
michael@0 1140
michael@0 1141 // Clip so we don't render outside the inner rect
michael@0 1142 aRenderingContext.PushState();
michael@0 1143 aRenderingContext.IntersectClip(inner);
michael@0 1144
michael@0 1145 // Check if we should display image placeholders
michael@0 1146 if (gIconLoad->mPrefShowPlaceholders) {
michael@0 1147 const nsStyleVisibility* vis = StyleVisibility();
michael@0 1148 nscoord size = nsPresContext::CSSPixelsToAppUnits(ICON_SIZE);
michael@0 1149
michael@0 1150 bool iconUsed = false;
michael@0 1151
michael@0 1152 // If we weren't previously displaying an icon, register ourselves
michael@0 1153 // as an observer for load and animation updates and flag that we're
michael@0 1154 // doing so now.
michael@0 1155 if (aRequest && !mDisplayingIcon) {
michael@0 1156 gIconLoad->AddIconObserver(this);
michael@0 1157 mDisplayingIcon = true;
michael@0 1158 }
michael@0 1159
michael@0 1160
michael@0 1161 // If the icon in question is loaded and decoded, draw it
michael@0 1162 uint32_t imageStatus = 0;
michael@0 1163 if (aRequest)
michael@0 1164 aRequest->GetImageStatus(&imageStatus);
michael@0 1165 if (imageStatus & imgIRequest::STATUS_FRAME_COMPLETE) {
michael@0 1166 nsCOMPtr<imgIContainer> imgCon;
michael@0 1167 aRequest->GetImage(getter_AddRefs(imgCon));
michael@0 1168 NS_ABORT_IF_FALSE(imgCon, "Frame Complete, but no image container?");
michael@0 1169 nsRect dest((vis->mDirection == NS_STYLE_DIRECTION_RTL) ?
michael@0 1170 inner.XMost() - size : inner.x,
michael@0 1171 inner.y, size, size);
michael@0 1172 nsLayoutUtils::DrawSingleImage(&aRenderingContext, imgCon,
michael@0 1173 nsLayoutUtils::GetGraphicsFilterForFrame(this), dest, aDirtyRect,
michael@0 1174 nullptr, imgIContainer::FLAG_NONE);
michael@0 1175 iconUsed = true;
michael@0 1176 }
michael@0 1177
michael@0 1178 // if we could not draw the icon, flag that we're waiting for it and
michael@0 1179 // just draw some graffiti in the mean time
michael@0 1180 if (!iconUsed) {
michael@0 1181 nscoord iconXPos = (vis->mDirection == NS_STYLE_DIRECTION_RTL) ?
michael@0 1182 inner.XMost() - size : inner.x;
michael@0 1183 nscoord twoPX = nsPresContext::CSSPixelsToAppUnits(2);
michael@0 1184 aRenderingContext.DrawRect(iconXPos, inner.y,size,size);
michael@0 1185 aRenderingContext.PushState();
michael@0 1186 aRenderingContext.SetColor(NS_RGB(0xFF,0,0));
michael@0 1187 aRenderingContext.FillEllipse(size/2 + iconXPos, size/2 + inner.y,
michael@0 1188 size/2 - twoPX, size/2 - twoPX);
michael@0 1189 aRenderingContext.PopState();
michael@0 1190 }
michael@0 1191
michael@0 1192 // Reduce the inner rect by the width of the icon, and leave an
michael@0 1193 // additional ICON_PADDING pixels for padding
michael@0 1194 int32_t iconWidth = nsPresContext::CSSPixelsToAppUnits(ICON_SIZE + ICON_PADDING);
michael@0 1195 if (vis->mDirection != NS_STYLE_DIRECTION_RTL)
michael@0 1196 inner.x += iconWidth;
michael@0 1197 inner.width -= iconWidth;
michael@0 1198 }
michael@0 1199
michael@0 1200 // If there's still room, display the alt-text
michael@0 1201 if (!inner.IsEmpty()) {
michael@0 1202 nsIContent* content = GetContent();
michael@0 1203 if (content) {
michael@0 1204 nsXPIDLString altText;
michael@0 1205 nsCSSFrameConstructor::GetAlternateTextFor(content, content->Tag(),
michael@0 1206 altText);
michael@0 1207 DisplayAltText(PresContext(), aRenderingContext, altText, inner);
michael@0 1208 }
michael@0 1209 }
michael@0 1210
michael@0 1211 aRenderingContext.PopState();
michael@0 1212 }
michael@0 1213
michael@0 1214 #ifdef DEBUG
michael@0 1215 static void PaintDebugImageMap(nsIFrame* aFrame, nsRenderingContext* aCtx,
michael@0 1216 const nsRect& aDirtyRect, nsPoint aPt) {
michael@0 1217 nsImageFrame* f = static_cast<nsImageFrame*>(aFrame);
michael@0 1218 nsRect inner = f->GetInnerArea() + aPt;
michael@0 1219
michael@0 1220 aCtx->SetColor(NS_RGB(0, 0, 0));
michael@0 1221 aCtx->PushState();
michael@0 1222 aCtx->Translate(inner.TopLeft());
michael@0 1223 f->GetImageMap()->Draw(aFrame, *aCtx);
michael@0 1224 aCtx->PopState();
michael@0 1225 }
michael@0 1226 #endif
michael@0 1227
michael@0 1228 void
michael@0 1229 nsDisplayImage::Paint(nsDisplayListBuilder* aBuilder,
michael@0 1230 nsRenderingContext* aCtx) {
michael@0 1231 uint32_t flags = imgIContainer::FLAG_NONE;
michael@0 1232 if (aBuilder->ShouldSyncDecodeImages()) {
michael@0 1233 flags |= imgIContainer::FLAG_SYNC_DECODE;
michael@0 1234 }
michael@0 1235 if (aBuilder->IsPaintingToWindow()) {
michael@0 1236 flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
michael@0 1237 }
michael@0 1238 static_cast<nsImageFrame*>(mFrame)->
michael@0 1239 PaintImage(*aCtx, ToReferenceFrame(), mVisibleRect, mImage, flags);
michael@0 1240 }
michael@0 1241
michael@0 1242 void
michael@0 1243 nsDisplayImage::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
michael@0 1244 const nsDisplayItemGeometry* aGeometry,
michael@0 1245 nsRegion* aInvalidRegion)
michael@0 1246 {
michael@0 1247 if (aBuilder->ShouldSyncDecodeImages() && mImage && !mImage->IsDecoded()) {
michael@0 1248 bool snap;
michael@0 1249 aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
michael@0 1250 }
michael@0 1251
michael@0 1252 nsDisplayImageContainer::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
michael@0 1253 }
michael@0 1254
michael@0 1255 already_AddRefed<ImageContainer>
michael@0 1256 nsDisplayImage::GetContainer(LayerManager* aManager,
michael@0 1257 nsDisplayListBuilder* aBuilder)
michael@0 1258 {
michael@0 1259 nsRefPtr<ImageContainer> container;
michael@0 1260 nsresult rv = mImage->GetImageContainer(aManager, getter_AddRefs(container));
michael@0 1261 NS_ENSURE_SUCCESS(rv, nullptr);
michael@0 1262 return container.forget();
michael@0 1263 }
michael@0 1264
michael@0 1265 gfxRect
michael@0 1266 nsDisplayImage::GetDestRect()
michael@0 1267 {
michael@0 1268 int32_t factor = mFrame->PresContext()->AppUnitsPerDevPixel();
michael@0 1269 nsImageFrame* imageFrame = static_cast<nsImageFrame*>(mFrame);
michael@0 1270
michael@0 1271 nsRect dest = imageFrame->GetInnerArea() + ToReferenceFrame();
michael@0 1272 gfxRect destRect(dest.x, dest.y, dest.width, dest.height);
michael@0 1273 destRect.ScaleInverse(factor);
michael@0 1274
michael@0 1275 return destRect;
michael@0 1276 }
michael@0 1277
michael@0 1278 LayerState
michael@0 1279 nsDisplayImage::GetLayerState(nsDisplayListBuilder* aBuilder,
michael@0 1280 LayerManager* aManager,
michael@0 1281 const ContainerLayerParameters& aParameters)
michael@0 1282 {
michael@0 1283 bool animated = false;
michael@0 1284 if (!nsLayoutUtils::AnimatedImageLayersEnabled() ||
michael@0 1285 mImage->GetType() != imgIContainer::TYPE_RASTER ||
michael@0 1286 NS_FAILED(mImage->GetAnimated(&animated)) ||
michael@0 1287 !animated) {
michael@0 1288 if (!aManager->IsCompositingCheap() ||
michael@0 1289 !nsLayoutUtils::GPUImageScalingEnabled()) {
michael@0 1290 return LAYER_NONE;
michael@0 1291 }
michael@0 1292 }
michael@0 1293
michael@0 1294 if (!animated) {
michael@0 1295 int32_t imageWidth;
michael@0 1296 int32_t imageHeight;
michael@0 1297 mImage->GetWidth(&imageWidth);
michael@0 1298 mImage->GetHeight(&imageHeight);
michael@0 1299
michael@0 1300 NS_ASSERTION(imageWidth != 0 && imageHeight != 0, "Invalid image size!");
michael@0 1301
michael@0 1302 gfxRect destRect = GetDestRect();
michael@0 1303
michael@0 1304 destRect.width *= aParameters.mXScale;
michael@0 1305 destRect.height *= aParameters.mYScale;
michael@0 1306
michael@0 1307 // Calculate the scaling factor for the frame.
michael@0 1308 gfxSize scale = gfxSize(destRect.width / imageWidth,
michael@0 1309 destRect.height / imageHeight);
michael@0 1310
michael@0 1311 // If we are not scaling at all, no point in separating this into a layer.
michael@0 1312 if (scale.width == 1.0f && scale.height == 1.0f) {
michael@0 1313 return LAYER_NONE;
michael@0 1314 }
michael@0 1315
michael@0 1316 // If the target size is pretty small, no point in using a layer.
michael@0 1317 if (destRect.width * destRect.height < 64 * 64) {
michael@0 1318 return LAYER_NONE;
michael@0 1319 }
michael@0 1320 }
michael@0 1321
michael@0 1322 nsRefPtr<ImageContainer> container;
michael@0 1323 mImage->GetImageContainer(aManager, getter_AddRefs(container));
michael@0 1324 if (!container) {
michael@0 1325 return LAYER_NONE;
michael@0 1326 }
michael@0 1327
michael@0 1328 return LAYER_ACTIVE;
michael@0 1329 }
michael@0 1330
michael@0 1331 already_AddRefed<Layer>
michael@0 1332 nsDisplayImage::BuildLayer(nsDisplayListBuilder* aBuilder,
michael@0 1333 LayerManager* aManager,
michael@0 1334 const ContainerLayerParameters& aParameters)
michael@0 1335 {
michael@0 1336 nsRefPtr<ImageContainer> container;
michael@0 1337 nsresult rv = mImage->GetImageContainer(aManager, getter_AddRefs(container));
michael@0 1338 NS_ENSURE_SUCCESS(rv, nullptr);
michael@0 1339
michael@0 1340 nsRefPtr<ImageLayer> layer = static_cast<ImageLayer*>
michael@0 1341 (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this));
michael@0 1342 if (!layer) {
michael@0 1343 layer = aManager->CreateImageLayer();
michael@0 1344 if (!layer)
michael@0 1345 return nullptr;
michael@0 1346 }
michael@0 1347 layer->SetContainer(container);
michael@0 1348 ConfigureLayer(layer, aParameters.mOffset);
michael@0 1349 return layer.forget();
michael@0 1350 }
michael@0 1351
michael@0 1352 void
michael@0 1353 nsDisplayImage::ConfigureLayer(ImageLayer *aLayer, const nsIntPoint& aOffset)
michael@0 1354 {
michael@0 1355 aLayer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(mFrame));
michael@0 1356
michael@0 1357 int32_t imageWidth;
michael@0 1358 int32_t imageHeight;
michael@0 1359 mImage->GetWidth(&imageWidth);
michael@0 1360 mImage->GetHeight(&imageHeight);
michael@0 1361
michael@0 1362 NS_ASSERTION(imageWidth != 0 && imageHeight != 0, "Invalid image size!");
michael@0 1363
michael@0 1364 const gfxRect destRect = GetDestRect();
michael@0 1365
michael@0 1366 gfx::Matrix transform;
michael@0 1367 gfxPoint p = destRect.TopLeft() + aOffset;
michael@0 1368 transform.Translate(p.x, p.y);
michael@0 1369 transform.Scale(destRect.Width()/imageWidth,
michael@0 1370 destRect.Height()/imageHeight);
michael@0 1371 aLayer->SetBaseTransform(gfx::Matrix4x4::From2D(transform));
michael@0 1372 aLayer->SetVisibleRegion(nsIntRect(0, 0, imageWidth, imageHeight));
michael@0 1373 }
michael@0 1374
michael@0 1375 void
michael@0 1376 nsImageFrame::PaintImage(nsRenderingContext& aRenderingContext, nsPoint aPt,
michael@0 1377 const nsRect& aDirtyRect, imgIContainer* aImage,
michael@0 1378 uint32_t aFlags)
michael@0 1379 {
michael@0 1380 // Render the image into our content area (the area inside
michael@0 1381 // the borders and padding)
michael@0 1382 NS_ASSERTION(GetInnerArea().width == mComputedSize.width, "bad width");
michael@0 1383 nsRect inner = GetInnerArea() + aPt;
michael@0 1384 nsRect dest(inner.TopLeft(), mComputedSize);
michael@0 1385 dest.y -= GetContinuationOffset();
michael@0 1386
michael@0 1387 nsLayoutUtils::DrawSingleImage(&aRenderingContext, aImage,
michael@0 1388 nsLayoutUtils::GetGraphicsFilterForFrame(this), dest, aDirtyRect,
michael@0 1389 nullptr, aFlags);
michael@0 1390
michael@0 1391 nsImageMap* map = GetImageMap();
michael@0 1392 if (nullptr != map) {
michael@0 1393 aRenderingContext.PushState();
michael@0 1394 aRenderingContext.Translate(inner.TopLeft());
michael@0 1395 aRenderingContext.SetColor(NS_RGB(255, 255, 255));
michael@0 1396 aRenderingContext.SetLineStyle(nsLineStyle_kSolid);
michael@0 1397 map->Draw(this, aRenderingContext);
michael@0 1398 aRenderingContext.SetColor(NS_RGB(0, 0, 0));
michael@0 1399 aRenderingContext.SetLineStyle(nsLineStyle_kDotted);
michael@0 1400 map->Draw(this, aRenderingContext);
michael@0 1401 aRenderingContext.PopState();
michael@0 1402 }
michael@0 1403 }
michael@0 1404
michael@0 1405 void
michael@0 1406 nsImageFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
michael@0 1407 const nsRect& aDirtyRect,
michael@0 1408 const nsDisplayListSet& aLists)
michael@0 1409 {
michael@0 1410 if (!IsVisibleForPainting(aBuilder))
michael@0 1411 return;
michael@0 1412
michael@0 1413 DisplayBorderBackgroundOutline(aBuilder, aLists);
michael@0 1414
michael@0 1415 DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox
michael@0 1416 clip(aBuilder, this, DisplayListClipState::ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT);
michael@0 1417
michael@0 1418 if (mComputedSize.width != 0 && mComputedSize.height != 0) {
michael@0 1419 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
michael@0 1420 NS_ASSERTION(imageLoader, "Not an image loading content?");
michael@0 1421
michael@0 1422 nsCOMPtr<imgIRequest> currentRequest;
michael@0 1423 if (imageLoader) {
michael@0 1424 imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
michael@0 1425 getter_AddRefs(currentRequest));
michael@0 1426 }
michael@0 1427
michael@0 1428 EventStates contentState = mContent->AsElement()->State();
michael@0 1429 bool imageOK = IMAGE_OK(contentState, true);
michael@0 1430
michael@0 1431 // XXX(seth): The SizeIsAvailable check here should not be necessary - the
michael@0 1432 // intention is that a non-null mImage means we have a size, but there is
michael@0 1433 // currently some code that violates this invariant.
michael@0 1434 if (!imageOK || !mImage || !SizeIsAvailable(currentRequest)) {
michael@0 1435 // No image yet, or image load failed. Draw the alt-text and an icon
michael@0 1436 // indicating the status
michael@0 1437 aLists.Content()->AppendNewToTop(new (aBuilder)
michael@0 1438 nsDisplayAltFeedback(aBuilder, this));
michael@0 1439 } else {
michael@0 1440 aLists.Content()->AppendNewToTop(new (aBuilder)
michael@0 1441 nsDisplayImage(aBuilder, this, mImage));
michael@0 1442
michael@0 1443 // If we were previously displaying an icon, we're not anymore
michael@0 1444 if (mDisplayingIcon) {
michael@0 1445 gIconLoad->RemoveIconObserver(this);
michael@0 1446 mDisplayingIcon = false;
michael@0 1447 }
michael@0 1448
michael@0 1449 #ifdef DEBUG
michael@0 1450 if (GetShowFrameBorders() && GetImageMap()) {
michael@0 1451 aLists.Outlines()->AppendNewToTop(new (aBuilder)
michael@0 1452 nsDisplayGeneric(aBuilder, this, PaintDebugImageMap, "DebugImageMap",
michael@0 1453 nsDisplayItem::TYPE_DEBUG_IMAGE_MAP));
michael@0 1454 }
michael@0 1455 #endif
michael@0 1456 }
michael@0 1457 }
michael@0 1458
michael@0 1459 if (ShouldDisplaySelection()) {
michael@0 1460 DisplaySelectionOverlay(aBuilder, aLists.Content(),
michael@0 1461 nsISelectionDisplay::DISPLAY_IMAGES);
michael@0 1462 }
michael@0 1463 }
michael@0 1464
michael@0 1465 bool
michael@0 1466 nsImageFrame::ShouldDisplaySelection()
michael@0 1467 {
michael@0 1468 // XXX what on EARTH is this code for?
michael@0 1469 nsresult result;
michael@0 1470 nsPresContext* presContext = PresContext();
michael@0 1471 int16_t displaySelection = presContext->PresShell()->GetSelectionFlags();
michael@0 1472 if (!(displaySelection & nsISelectionDisplay::DISPLAY_IMAGES))
michael@0 1473 return false;//no need to check the blue border, we cannot be drawn selected
michael@0 1474 //insert hook here for image selection drawing
michael@0 1475 #if IMAGE_EDITOR_CHECK
michael@0 1476 //check to see if this frame is in an editor context
michael@0 1477 //isEditor check. this needs to be changed to have better way to check
michael@0 1478 if (displaySelection == nsISelectionDisplay::DISPLAY_ALL)
michael@0 1479 {
michael@0 1480 nsCOMPtr<nsISelectionController> selCon;
michael@0 1481 result = GetSelectionController(presContext, getter_AddRefs(selCon));
michael@0 1482 if (NS_SUCCEEDED(result) && selCon)
michael@0 1483 {
michael@0 1484 nsCOMPtr<nsISelection> selection;
michael@0 1485 result = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
michael@0 1486 if (NS_SUCCEEDED(result) && selection)
michael@0 1487 {
michael@0 1488 int32_t rangeCount;
michael@0 1489 selection->GetRangeCount(&rangeCount);
michael@0 1490 if (rangeCount == 1) //if not one then let code drop to nsFrame::Paint
michael@0 1491 {
michael@0 1492 nsCOMPtr<nsIContent> parentContent = mContent->GetParent();
michael@0 1493 if (parentContent)
michael@0 1494 {
michael@0 1495 int32_t thisOffset = parentContent->IndexOf(mContent);
michael@0 1496 nsCOMPtr<nsIDOMNode> parentNode = do_QueryInterface(parentContent);
michael@0 1497 nsCOMPtr<nsIDOMNode> rangeNode;
michael@0 1498 int32_t rangeOffset;
michael@0 1499 nsCOMPtr<nsIDOMRange> range;
michael@0 1500 selection->GetRangeAt(0,getter_AddRefs(range));
michael@0 1501 if (range)
michael@0 1502 {
michael@0 1503 range->GetStartContainer(getter_AddRefs(rangeNode));
michael@0 1504 range->GetStartOffset(&rangeOffset);
michael@0 1505
michael@0 1506 if (parentNode && rangeNode && (rangeNode == parentNode) && rangeOffset == thisOffset)
michael@0 1507 {
michael@0 1508 range->GetEndContainer(getter_AddRefs(rangeNode));
michael@0 1509 range->GetEndOffset(&rangeOffset);
michael@0 1510 if ((rangeNode == parentNode) && (rangeOffset == (thisOffset +1))) //+1 since that would mean this whole content is selected only
michael@0 1511 return false; //do not allow nsFrame do draw any further selection
michael@0 1512 }
michael@0 1513 }
michael@0 1514 }
michael@0 1515 }
michael@0 1516 }
michael@0 1517 }
michael@0 1518 }
michael@0 1519 #endif
michael@0 1520 return true;
michael@0 1521 }
michael@0 1522
michael@0 1523 nsImageMap*
michael@0 1524 nsImageFrame::GetImageMap()
michael@0 1525 {
michael@0 1526 if (!mImageMap) {
michael@0 1527 nsIContent* map = GetMapElement();
michael@0 1528 if (map) {
michael@0 1529 mImageMap = new nsImageMap();
michael@0 1530 NS_ADDREF(mImageMap);
michael@0 1531 mImageMap->Init(this, map);
michael@0 1532 }
michael@0 1533 }
michael@0 1534
michael@0 1535 return mImageMap;
michael@0 1536 }
michael@0 1537
michael@0 1538 bool
michael@0 1539 nsImageFrame::IsServerImageMap()
michael@0 1540 {
michael@0 1541 return mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::ismap);
michael@0 1542 }
michael@0 1543
michael@0 1544 // Translate an point that is relative to our frame
michael@0 1545 // into a localized pixel coordinate that is relative to the
michael@0 1546 // content area of this frame (inside the border+padding).
michael@0 1547 void
michael@0 1548 nsImageFrame::TranslateEventCoords(const nsPoint& aPoint,
michael@0 1549 nsIntPoint& aResult)
michael@0 1550 {
michael@0 1551 nscoord x = aPoint.x;
michael@0 1552 nscoord y = aPoint.y;
michael@0 1553
michael@0 1554 // Subtract out border and padding here so that the coordinates are
michael@0 1555 // now relative to the content area of this frame.
michael@0 1556 nsRect inner = GetInnerArea();
michael@0 1557 x -= inner.x;
michael@0 1558 y -= inner.y;
michael@0 1559
michael@0 1560 aResult.x = nsPresContext::AppUnitsToIntCSSPixels(x);
michael@0 1561 aResult.y = nsPresContext::AppUnitsToIntCSSPixels(y);
michael@0 1562 }
michael@0 1563
michael@0 1564 bool
michael@0 1565 nsImageFrame::GetAnchorHREFTargetAndNode(nsIURI** aHref, nsString& aTarget,
michael@0 1566 nsIContent** aNode)
michael@0 1567 {
michael@0 1568 bool status = false;
michael@0 1569 aTarget.Truncate();
michael@0 1570 *aHref = nullptr;
michael@0 1571 *aNode = nullptr;
michael@0 1572
michael@0 1573 // Walk up the content tree, looking for an nsIDOMAnchorElement
michael@0 1574 for (nsIContent* content = mContent->GetParent();
michael@0 1575 content; content = content->GetParent()) {
michael@0 1576 nsCOMPtr<dom::Link> link(do_QueryInterface(content));
michael@0 1577 if (link) {
michael@0 1578 nsCOMPtr<nsIURI> href = content->GetHrefURI();
michael@0 1579 if (href) {
michael@0 1580 href->Clone(aHref);
michael@0 1581 }
michael@0 1582 status = (*aHref != nullptr);
michael@0 1583
michael@0 1584 nsCOMPtr<nsIDOMHTMLAnchorElement> anchor(do_QueryInterface(content));
michael@0 1585 if (anchor) {
michael@0 1586 anchor->GetTarget(aTarget);
michael@0 1587 }
michael@0 1588 NS_ADDREF(*aNode = content);
michael@0 1589 break;
michael@0 1590 }
michael@0 1591 }
michael@0 1592 return status;
michael@0 1593 }
michael@0 1594
michael@0 1595 nsresult
michael@0 1596 nsImageFrame::GetContentForEvent(WidgetEvent* aEvent,
michael@0 1597 nsIContent** aContent)
michael@0 1598 {
michael@0 1599 NS_ENSURE_ARG_POINTER(aContent);
michael@0 1600
michael@0 1601 nsIFrame* f = nsLayoutUtils::GetNonGeneratedAncestor(this);
michael@0 1602 if (f != this) {
michael@0 1603 return f->GetContentForEvent(aEvent, aContent);
michael@0 1604 }
michael@0 1605
michael@0 1606 // XXX We need to make this special check for area element's capturing the
michael@0 1607 // mouse due to bug 135040. Remove it once that's fixed.
michael@0 1608 nsIContent* capturingContent =
michael@0 1609 aEvent->HasMouseEventMessage() ? nsIPresShell::GetCapturingContent() :
michael@0 1610 nullptr;
michael@0 1611 if (capturingContent && capturingContent->GetPrimaryFrame() == this) {
michael@0 1612 *aContent = capturingContent;
michael@0 1613 NS_IF_ADDREF(*aContent);
michael@0 1614 return NS_OK;
michael@0 1615 }
michael@0 1616
michael@0 1617 nsImageMap* map = GetImageMap();
michael@0 1618
michael@0 1619 if (nullptr != map) {
michael@0 1620 nsIntPoint p;
michael@0 1621 TranslateEventCoords(
michael@0 1622 nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this), p);
michael@0 1623 nsCOMPtr<nsIContent> area = map->GetArea(p.x, p.y);
michael@0 1624 if (area) {
michael@0 1625 area.forget(aContent);
michael@0 1626 return NS_OK;
michael@0 1627 }
michael@0 1628 }
michael@0 1629
michael@0 1630 *aContent = GetContent();
michael@0 1631 NS_IF_ADDREF(*aContent);
michael@0 1632 return NS_OK;
michael@0 1633 }
michael@0 1634
michael@0 1635 // XXX what should clicks on transparent pixels do?
michael@0 1636 nsresult
michael@0 1637 nsImageFrame::HandleEvent(nsPresContext* aPresContext,
michael@0 1638 WidgetGUIEvent* aEvent,
michael@0 1639 nsEventStatus* aEventStatus)
michael@0 1640 {
michael@0 1641 NS_ENSURE_ARG_POINTER(aEventStatus);
michael@0 1642
michael@0 1643 if ((aEvent->message == NS_MOUSE_BUTTON_UP &&
michael@0 1644 aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) ||
michael@0 1645 aEvent->message == NS_MOUSE_MOVE) {
michael@0 1646 nsImageMap* map = GetImageMap();
michael@0 1647 bool isServerMap = IsServerImageMap();
michael@0 1648 if ((nullptr != map) || isServerMap) {
michael@0 1649 nsIntPoint p;
michael@0 1650 TranslateEventCoords(
michael@0 1651 nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this), p);
michael@0 1652 bool inside = false;
michael@0 1653 // Even though client-side image map triggering happens
michael@0 1654 // through content, we need to make sure we're not inside
michael@0 1655 // (in case we deal with a case of both client-side and
michael@0 1656 // sever-side on the same image - it happens!)
michael@0 1657 if (nullptr != map) {
michael@0 1658 inside = !!map->GetArea(p.x, p.y);
michael@0 1659 }
michael@0 1660
michael@0 1661 if (!inside && isServerMap) {
michael@0 1662
michael@0 1663 // Server side image maps use the href in a containing anchor
michael@0 1664 // element to provide the basis for the destination url.
michael@0 1665 nsCOMPtr<nsIURI> uri;
michael@0 1666 nsAutoString target;
michael@0 1667 nsCOMPtr<nsIContent> anchorNode;
michael@0 1668 if (GetAnchorHREFTargetAndNode(getter_AddRefs(uri), target,
michael@0 1669 getter_AddRefs(anchorNode))) {
michael@0 1670 // XXX if the mouse is over/clicked in the border/padding area
michael@0 1671 // we should probably just pretend nothing happened. Nav4
michael@0 1672 // keeps the x,y coordinates positive as we do; IE doesn't
michael@0 1673 // bother. Both of them send the click through even when the
michael@0 1674 // mouse is over the border.
michael@0 1675 if (p.x < 0) p.x = 0;
michael@0 1676 if (p.y < 0) p.y = 0;
michael@0 1677 nsAutoCString spec;
michael@0 1678 uri->GetSpec(spec);
michael@0 1679 spec += nsPrintfCString("?%d,%d", p.x, p.y);
michael@0 1680 uri->SetSpec(spec);
michael@0 1681
michael@0 1682 bool clicked = false;
michael@0 1683 if (aEvent->message == NS_MOUSE_BUTTON_UP) {
michael@0 1684 *aEventStatus = nsEventStatus_eConsumeDoDefault;
michael@0 1685 clicked = true;
michael@0 1686 }
michael@0 1687 nsContentUtils::TriggerLink(anchorNode, aPresContext, uri, target,
michael@0 1688 clicked, true, true);
michael@0 1689 }
michael@0 1690 }
michael@0 1691 }
michael@0 1692 }
michael@0 1693
michael@0 1694 return nsSplittableFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
michael@0 1695 }
michael@0 1696
michael@0 1697 nsresult
michael@0 1698 nsImageFrame::GetCursor(const nsPoint& aPoint,
michael@0 1699 nsIFrame::Cursor& aCursor)
michael@0 1700 {
michael@0 1701 nsImageMap* map = GetImageMap();
michael@0 1702 if (nullptr != map) {
michael@0 1703 nsIntPoint p;
michael@0 1704 TranslateEventCoords(aPoint, p);
michael@0 1705 nsCOMPtr<nsIContent> area = map->GetArea(p.x, p.y);
michael@0 1706 if (area) {
michael@0 1707 // Use the cursor from the style of the *area* element.
michael@0 1708 // XXX Using the image as the parent style context isn't
michael@0 1709 // technically correct, but it's probably the right thing to do
michael@0 1710 // here, since it means that areas on which the cursor isn't
michael@0 1711 // specified will inherit the style from the image.
michael@0 1712 nsRefPtr<nsStyleContext> areaStyle =
michael@0 1713 PresContext()->PresShell()->StyleSet()->
michael@0 1714 ResolveStyleFor(area->AsElement(), StyleContext());
michael@0 1715 FillCursorInformationFromStyle(areaStyle->StyleUserInterface(),
michael@0 1716 aCursor);
michael@0 1717 if (NS_STYLE_CURSOR_AUTO == aCursor.mCursor) {
michael@0 1718 aCursor.mCursor = NS_STYLE_CURSOR_DEFAULT;
michael@0 1719 }
michael@0 1720 return NS_OK;
michael@0 1721 }
michael@0 1722 }
michael@0 1723 return nsFrame::GetCursor(aPoint, aCursor);
michael@0 1724 }
michael@0 1725
michael@0 1726 nsresult
michael@0 1727 nsImageFrame::AttributeChanged(int32_t aNameSpaceID,
michael@0 1728 nsIAtom* aAttribute,
michael@0 1729 int32_t aModType)
michael@0 1730 {
michael@0 1731 nsresult rv = nsSplittableFrame::AttributeChanged(aNameSpaceID,
michael@0 1732 aAttribute, aModType);
michael@0 1733 if (NS_FAILED(rv)) {
michael@0 1734 return rv;
michael@0 1735 }
michael@0 1736 if (nsGkAtoms::alt == aAttribute)
michael@0 1737 {
michael@0 1738 PresContext()->PresShell()->FrameNeedsReflow(this,
michael@0 1739 nsIPresShell::eStyleChange,
michael@0 1740 NS_FRAME_IS_DIRTY);
michael@0 1741 }
michael@0 1742
michael@0 1743 return NS_OK;
michael@0 1744 }
michael@0 1745
michael@0 1746 nsIAtom*
michael@0 1747 nsImageFrame::GetType() const
michael@0 1748 {
michael@0 1749 return nsGkAtoms::imageFrame;
michael@0 1750 }
michael@0 1751
michael@0 1752 #ifdef DEBUG_FRAME_DUMP
michael@0 1753 nsresult
michael@0 1754 nsImageFrame::GetFrameName(nsAString& aResult) const
michael@0 1755 {
michael@0 1756 return MakeFrameName(NS_LITERAL_STRING("ImageFrame"), aResult);
michael@0 1757 }
michael@0 1758
michael@0 1759 void
michael@0 1760 nsImageFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const
michael@0 1761 {
michael@0 1762 nsCString str;
michael@0 1763 ListGeneric(str, aPrefix, aFlags);
michael@0 1764
michael@0 1765 // output the img src url
michael@0 1766 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
michael@0 1767 if (imageLoader) {
michael@0 1768 nsCOMPtr<imgIRequest> currentRequest;
michael@0 1769 imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
michael@0 1770 getter_AddRefs(currentRequest));
michael@0 1771 if (currentRequest) {
michael@0 1772 nsCOMPtr<nsIURI> uri;
michael@0 1773 currentRequest->GetURI(getter_AddRefs(uri));
michael@0 1774 nsAutoCString uristr;
michael@0 1775 uri->GetAsciiSpec(uristr);
michael@0 1776 str += nsPrintfCString(" [src=%s]", uristr.get());
michael@0 1777 }
michael@0 1778 }
michael@0 1779 fprintf_stderr(out, "%s\n", str.get());
michael@0 1780 }
michael@0 1781 #endif
michael@0 1782
michael@0 1783 int
michael@0 1784 nsImageFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const
michael@0 1785 {
michael@0 1786 int skip = 0;
michael@0 1787 if (nullptr != GetPrevInFlow()) {
michael@0 1788 skip |= LOGICAL_SIDE_B_START;
michael@0 1789 }
michael@0 1790 if (nullptr != GetNextInFlow()) {
michael@0 1791 skip |= LOGICAL_SIDE_B_END;
michael@0 1792 }
michael@0 1793 return skip;
michael@0 1794 }
michael@0 1795
michael@0 1796 nsresult
michael@0 1797 nsImageFrame::GetIntrinsicImageSize(nsSize& aSize)
michael@0 1798 {
michael@0 1799 if (mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord &&
michael@0 1800 mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord) {
michael@0 1801 aSize.SizeTo(mIntrinsicSize.width.GetCoordValue(),
michael@0 1802 mIntrinsicSize.height.GetCoordValue());
michael@0 1803 return NS_OK;
michael@0 1804 }
michael@0 1805
michael@0 1806 return NS_ERROR_FAILURE;
michael@0 1807 }
michael@0 1808
michael@0 1809 nsresult
michael@0 1810 nsImageFrame::LoadIcon(const nsAString& aSpec,
michael@0 1811 nsPresContext *aPresContext,
michael@0 1812 imgRequestProxy** aRequest)
michael@0 1813 {
michael@0 1814 nsresult rv = NS_OK;
michael@0 1815 NS_PRECONDITION(!aSpec.IsEmpty(), "What happened??");
michael@0 1816 NS_PRECONDITION(aPresContext, "NULL PresContext");
michael@0 1817
michael@0 1818 if (!sIOService) {
michael@0 1819 rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
michael@0 1820 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1821 }
michael@0 1822
michael@0 1823 nsCOMPtr<nsIURI> realURI;
michael@0 1824 SpecToURI(aSpec, sIOService, getter_AddRefs(realURI));
michael@0 1825
michael@0 1826 nsRefPtr<imgLoader> il =
michael@0 1827 nsContentUtils::GetImgLoaderForDocument(aPresContext->Document());
michael@0 1828
michael@0 1829 nsCOMPtr<nsILoadGroup> loadGroup;
michael@0 1830 GetLoadGroup(aPresContext, getter_AddRefs(loadGroup));
michael@0 1831
michael@0 1832 // For icon loads, we don't need to merge with the loadgroup flags
michael@0 1833 nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
michael@0 1834
michael@0 1835 nsCOMPtr<nsIURI> firstPartyIsolationURI;
michael@0 1836 nsCOMPtr<mozIThirdPartyUtil> thirdPartySvc
michael@0 1837 = do_GetService(THIRDPARTYUTIL_CONTRACTID);
michael@0 1838 // XXX: Should we pass the loadgroup, too? Is document ever likely
michael@0 1839 // to be unset?
michael@0 1840 thirdPartySvc->GetFirstPartyIsolationURI(nullptr, aPresContext->Document(),
michael@0 1841 getter_AddRefs(firstPartyIsolationURI));
michael@0 1842
michael@0 1843 return il->LoadImage(realURI, /* icon URI */
michael@0 1844 firstPartyIsolationURI, /* initial document URI; this is only
michael@0 1845 relevant for cookies, so does not
michael@0 1846 apply to icons. */
michael@0 1847 nullptr, /* referrer (not relevant for icons) */
michael@0 1848 nullptr, /* principal (not relevant for icons) */
michael@0 1849 loadGroup,
michael@0 1850 gIconLoad,
michael@0 1851 nullptr, /* Not associated with any particular document */
michael@0 1852 loadFlags,
michael@0 1853 nullptr,
michael@0 1854 nullptr, /* channel policy not needed */
michael@0 1855 EmptyString(),
michael@0 1856 aRequest);
michael@0 1857 }
michael@0 1858
michael@0 1859 void
michael@0 1860 nsImageFrame::GetDocumentCharacterSet(nsACString& aCharset) const
michael@0 1861 {
michael@0 1862 if (mContent) {
michael@0 1863 NS_ASSERTION(mContent->GetDocument(),
michael@0 1864 "Frame still alive after content removed from document!");
michael@0 1865 aCharset = mContent->GetDocument()->GetDocumentCharacterSet();
michael@0 1866 }
michael@0 1867 }
michael@0 1868
michael@0 1869 void
michael@0 1870 nsImageFrame::SpecToURI(const nsAString& aSpec, nsIIOService *aIOService,
michael@0 1871 nsIURI **aURI)
michael@0 1872 {
michael@0 1873 nsCOMPtr<nsIURI> baseURI;
michael@0 1874 if (mContent) {
michael@0 1875 baseURI = mContent->GetBaseURI();
michael@0 1876 }
michael@0 1877 nsAutoCString charset;
michael@0 1878 GetDocumentCharacterSet(charset);
michael@0 1879 NS_NewURI(aURI, aSpec,
michael@0 1880 charset.IsEmpty() ? nullptr : charset.get(),
michael@0 1881 baseURI, aIOService);
michael@0 1882 }
michael@0 1883
michael@0 1884 void
michael@0 1885 nsImageFrame::GetLoadGroup(nsPresContext *aPresContext, nsILoadGroup **aLoadGroup)
michael@0 1886 {
michael@0 1887 if (!aPresContext)
michael@0 1888 return;
michael@0 1889
michael@0 1890 NS_PRECONDITION(nullptr != aLoadGroup, "null OUT parameter pointer");
michael@0 1891
michael@0 1892 nsIPresShell *shell = aPresContext->GetPresShell();
michael@0 1893
michael@0 1894 if (!shell)
michael@0 1895 return;
michael@0 1896
michael@0 1897 nsIDocument *doc = shell->GetDocument();
michael@0 1898 if (!doc)
michael@0 1899 return;
michael@0 1900
michael@0 1901 *aLoadGroup = doc->GetDocumentLoadGroup().take();
michael@0 1902 }
michael@0 1903
michael@0 1904 nsresult nsImageFrame::LoadIcons(nsPresContext *aPresContext)
michael@0 1905 {
michael@0 1906 NS_ASSERTION(!gIconLoad, "called LoadIcons twice");
michael@0 1907
michael@0 1908 NS_NAMED_LITERAL_STRING(loadingSrc,"resource://gre-resources/loading-image.png");
michael@0 1909 NS_NAMED_LITERAL_STRING(brokenSrc,"resource://gre-resources/broken-image.png");
michael@0 1910
michael@0 1911 gIconLoad = new IconLoad();
michael@0 1912 NS_ADDREF(gIconLoad);
michael@0 1913
michael@0 1914 nsresult rv;
michael@0 1915 // create a loader and load the images
michael@0 1916 rv = LoadIcon(loadingSrc,
michael@0 1917 aPresContext,
michael@0 1918 getter_AddRefs(gIconLoad->mLoadingImage));
michael@0 1919 if (NS_FAILED(rv)) {
michael@0 1920 return rv;
michael@0 1921 }
michael@0 1922
michael@0 1923 rv = LoadIcon(brokenSrc,
michael@0 1924 aPresContext,
michael@0 1925 getter_AddRefs(gIconLoad->mBrokenImage));
michael@0 1926 return rv;
michael@0 1927 }
michael@0 1928
michael@0 1929 NS_IMPL_ISUPPORTS(nsImageFrame::IconLoad, nsIObserver,
michael@0 1930 imgINotificationObserver)
michael@0 1931
michael@0 1932 static const char* kIconLoadPrefs[] = {
michael@0 1933 "browser.display.force_inline_alttext",
michael@0 1934 "browser.display.show_image_placeholders",
michael@0 1935 nullptr
michael@0 1936 };
michael@0 1937
michael@0 1938 nsImageFrame::IconLoad::IconLoad()
michael@0 1939 {
michael@0 1940 // register observers
michael@0 1941 Preferences::AddStrongObservers(this, kIconLoadPrefs);
michael@0 1942 GetPrefs();
michael@0 1943 }
michael@0 1944
michael@0 1945 void
michael@0 1946 nsImageFrame::IconLoad::Shutdown()
michael@0 1947 {
michael@0 1948 Preferences::RemoveObservers(this, kIconLoadPrefs);
michael@0 1949 // in case the pref service releases us later
michael@0 1950 if (mLoadingImage) {
michael@0 1951 mLoadingImage->CancelAndForgetObserver(NS_ERROR_FAILURE);
michael@0 1952 mLoadingImage = nullptr;
michael@0 1953 }
michael@0 1954 if (mBrokenImage) {
michael@0 1955 mBrokenImage->CancelAndForgetObserver(NS_ERROR_FAILURE);
michael@0 1956 mBrokenImage = nullptr;
michael@0 1957 }
michael@0 1958 }
michael@0 1959
michael@0 1960 NS_IMETHODIMP
michael@0 1961 nsImageFrame::IconLoad::Observe(nsISupports *aSubject, const char* aTopic,
michael@0 1962 const char16_t* aData)
michael@0 1963 {
michael@0 1964 NS_ASSERTION(!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID),
michael@0 1965 "wrong topic");
michael@0 1966 #ifdef DEBUG
michael@0 1967 // assert |aData| is one of our prefs.
michael@0 1968 for (uint32_t i = 0; i < ArrayLength(kIconLoadPrefs) ||
michael@0 1969 (NS_NOTREACHED("wrong pref"), false); ++i)
michael@0 1970 if (NS_ConvertASCIItoUTF16(kIconLoadPrefs[i]) == nsDependentString(aData))
michael@0 1971 break;
michael@0 1972 #endif
michael@0 1973
michael@0 1974 GetPrefs();
michael@0 1975 return NS_OK;
michael@0 1976 }
michael@0 1977
michael@0 1978 void nsImageFrame::IconLoad::GetPrefs()
michael@0 1979 {
michael@0 1980 mPrefForceInlineAltText =
michael@0 1981 Preferences::GetBool("browser.display.force_inline_alttext");
michael@0 1982
michael@0 1983 mPrefShowPlaceholders =
michael@0 1984 Preferences::GetBool("browser.display.show_image_placeholders", true);
michael@0 1985 }
michael@0 1986
michael@0 1987 NS_IMETHODIMP
michael@0 1988 nsImageFrame::IconLoad::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
michael@0 1989 {
michael@0 1990 if (aType != imgINotificationObserver::LOAD_COMPLETE &&
michael@0 1991 aType != imgINotificationObserver::FRAME_UPDATE) {
michael@0 1992 return NS_OK;
michael@0 1993 }
michael@0 1994
michael@0 1995 nsTObserverArray<nsImageFrame*>::ForwardIterator iter(mIconObservers);
michael@0 1996 nsImageFrame *frame;
michael@0 1997 while (iter.HasMore()) {
michael@0 1998 frame = iter.GetNext();
michael@0 1999 frame->InvalidateFrame();
michael@0 2000 }
michael@0 2001
michael@0 2002 return NS_OK;
michael@0 2003 }
michael@0 2004
michael@0 2005 NS_IMPL_ISUPPORTS(nsImageListener, imgINotificationObserver)
michael@0 2006
michael@0 2007 nsImageListener::nsImageListener(nsImageFrame *aFrame) :
michael@0 2008 mFrame(aFrame)
michael@0 2009 {
michael@0 2010 }
michael@0 2011
michael@0 2012 nsImageListener::~nsImageListener()
michael@0 2013 {
michael@0 2014 }
michael@0 2015
michael@0 2016 NS_IMETHODIMP
michael@0 2017 nsImageListener::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
michael@0 2018 {
michael@0 2019 if (!mFrame)
michael@0 2020 return NS_ERROR_FAILURE;
michael@0 2021
michael@0 2022 return mFrame->Notify(aRequest, aType, aData);
michael@0 2023 }
michael@0 2024
michael@0 2025 static bool
michael@0 2026 IsInAutoWidthTableCellForQuirk(nsIFrame *aFrame)
michael@0 2027 {
michael@0 2028 if (eCompatibility_NavQuirks != aFrame->PresContext()->CompatibilityMode())
michael@0 2029 return false;
michael@0 2030 // Check if the parent of the closest nsBlockFrame has auto width.
michael@0 2031 nsBlockFrame *ancestor = nsLayoutUtils::FindNearestBlockAncestor(aFrame);
michael@0 2032 if (ancestor->StyleContext()->GetPseudo() == nsCSSAnonBoxes::cellContent) {
michael@0 2033 // Assume direct parent is a table cell frame.
michael@0 2034 nsFrame *grandAncestor = static_cast<nsFrame*>(ancestor->GetParent());
michael@0 2035 return grandAncestor &&
michael@0 2036 grandAncestor->StylePosition()->mWidth.GetUnit() == eStyleUnit_Auto;
michael@0 2037 }
michael@0 2038 return false;
michael@0 2039 }
michael@0 2040
michael@0 2041 /* virtual */ void
michael@0 2042 nsImageFrame::AddInlineMinWidth(nsRenderingContext *aRenderingContext,
michael@0 2043 nsIFrame::InlineMinWidthData *aData)
michael@0 2044 {
michael@0 2045
michael@0 2046 NS_ASSERTION(GetParent(), "Must have a parent if we get here!");
michael@0 2047
michael@0 2048 nsIFrame* parent = GetParent();
michael@0 2049 bool canBreak =
michael@0 2050 !CanContinueTextRun() &&
michael@0 2051 parent->StyleText()->WhiteSpaceCanWrap(parent) &&
michael@0 2052 !IsInAutoWidthTableCellForQuirk(this);
michael@0 2053
michael@0 2054 if (canBreak)
michael@0 2055 aData->OptionallyBreak(aRenderingContext);
michael@0 2056
michael@0 2057 aData->trailingWhitespace = 0;
michael@0 2058 aData->skipWhitespace = false;
michael@0 2059 aData->trailingTextFrame = nullptr;
michael@0 2060 aData->currentLine += nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
michael@0 2061 this, nsLayoutUtils::MIN_WIDTH);
michael@0 2062 aData->atStartOfLine = false;
michael@0 2063
michael@0 2064 if (canBreak)
michael@0 2065 aData->OptionallyBreak(aRenderingContext);
michael@0 2066
michael@0 2067 }

mercurial