Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | /* 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 | } |