1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/forms/nsButtonFrameRenderer.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,408 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 +#include "nsButtonFrameRenderer.h" 1.9 +#include "nsCSSRendering.h" 1.10 +#include "nsPresContext.h" 1.11 +#include "nsGkAtoms.h" 1.12 +#include "nsCSSPseudoElements.h" 1.13 +#include "nsNameSpaceManager.h" 1.14 +#include "nsStyleSet.h" 1.15 +#include "nsDisplayList.h" 1.16 +#include "nsITheme.h" 1.17 +#include "nsFrame.h" 1.18 +#include "mozilla/EventStates.h" 1.19 +#include "mozilla/dom/Element.h" 1.20 + 1.21 +#define ACTIVE "active" 1.22 +#define HOVER "hover" 1.23 +#define FOCUS "focus" 1.24 + 1.25 +nsButtonFrameRenderer::nsButtonFrameRenderer() 1.26 +{ 1.27 + MOZ_COUNT_CTOR(nsButtonFrameRenderer); 1.28 +} 1.29 + 1.30 +nsButtonFrameRenderer::~nsButtonFrameRenderer() 1.31 +{ 1.32 + MOZ_COUNT_DTOR(nsButtonFrameRenderer); 1.33 +} 1.34 + 1.35 +void 1.36 +nsButtonFrameRenderer::SetFrame(nsFrame* aFrame, nsPresContext* aPresContext) 1.37 +{ 1.38 + mFrame = aFrame; 1.39 + ReResolveStyles(aPresContext); 1.40 +} 1.41 + 1.42 +nsIFrame* 1.43 +nsButtonFrameRenderer::GetFrame() 1.44 +{ 1.45 + return mFrame; 1.46 +} 1.47 + 1.48 +void 1.49 +nsButtonFrameRenderer::SetDisabled(bool aDisabled, bool notify) 1.50 +{ 1.51 + if (aDisabled) 1.52 + mFrame->GetContent()->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled, EmptyString(), 1.53 + notify); 1.54 + else 1.55 + mFrame->GetContent()->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, notify); 1.56 +} 1.57 + 1.58 +bool 1.59 +nsButtonFrameRenderer::isDisabled() 1.60 +{ 1.61 + return mFrame->GetContent()->AsElement()-> 1.62 + State().HasState(NS_EVENT_STATE_DISABLED); 1.63 +} 1.64 + 1.65 +class nsDisplayButtonBoxShadowOuter : public nsDisplayItem { 1.66 +public: 1.67 + nsDisplayButtonBoxShadowOuter(nsDisplayListBuilder* aBuilder, 1.68 + nsButtonFrameRenderer* aRenderer) 1.69 + : nsDisplayItem(aBuilder, aRenderer->GetFrame()), mBFR(aRenderer) { 1.70 + MOZ_COUNT_CTOR(nsDisplayButtonBoxShadowOuter); 1.71 + } 1.72 +#ifdef NS_BUILD_REFCNT_LOGGING 1.73 + virtual ~nsDisplayButtonBoxShadowOuter() { 1.74 + MOZ_COUNT_DTOR(nsDisplayButtonBoxShadowOuter); 1.75 + } 1.76 +#endif 1.77 + 1.78 + virtual void Paint(nsDisplayListBuilder* aBuilder, 1.79 + nsRenderingContext* aCtx) MOZ_OVERRIDE; 1.80 + virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, 1.81 + bool* aSnap) MOZ_OVERRIDE; 1.82 + NS_DISPLAY_DECL_NAME("ButtonBoxShadowOuter", TYPE_BUTTON_BOX_SHADOW_OUTER) 1.83 +private: 1.84 + nsButtonFrameRenderer* mBFR; 1.85 +}; 1.86 + 1.87 +nsRect 1.88 +nsDisplayButtonBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) { 1.89 + *aSnap = false; 1.90 + return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame(); 1.91 +} 1.92 + 1.93 +void 1.94 +nsDisplayButtonBoxShadowOuter::Paint(nsDisplayListBuilder* aBuilder, 1.95 + nsRenderingContext* aCtx) { 1.96 + nsRect frameRect = nsRect(ToReferenceFrame(), mFrame->GetSize()); 1.97 + 1.98 + nsRect buttonRect; 1.99 + mBFR->GetButtonRect(frameRect, buttonRect); 1.100 + 1.101 + nsCSSRendering::PaintBoxShadowOuter(mFrame->PresContext(), *aCtx, mFrame, 1.102 + buttonRect, mVisibleRect); 1.103 +} 1.104 + 1.105 +class nsDisplayButtonBorderBackground : public nsDisplayItem { 1.106 +public: 1.107 + nsDisplayButtonBorderBackground(nsDisplayListBuilder* aBuilder, 1.108 + nsButtonFrameRenderer* aRenderer) 1.109 + : nsDisplayItem(aBuilder, aRenderer->GetFrame()), mBFR(aRenderer) { 1.110 + MOZ_COUNT_CTOR(nsDisplayButtonBorderBackground); 1.111 + } 1.112 +#ifdef NS_BUILD_REFCNT_LOGGING 1.113 + virtual ~nsDisplayButtonBorderBackground() { 1.114 + MOZ_COUNT_DTOR(nsDisplayButtonBorderBackground); 1.115 + } 1.116 +#endif 1.117 + 1.118 + virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, 1.119 + HitTestState* aState, 1.120 + nsTArray<nsIFrame*> *aOutFrames) MOZ_OVERRIDE { 1.121 + aOutFrames->AppendElement(mFrame); 1.122 + } 1.123 + virtual void Paint(nsDisplayListBuilder* aBuilder, 1.124 + nsRenderingContext* aCtx) MOZ_OVERRIDE; 1.125 + virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, 1.126 + bool* aSnap) MOZ_OVERRIDE; 1.127 + virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, 1.128 + const nsDisplayItemGeometry* aGeometry, 1.129 + nsRegion *aInvalidRegion) MOZ_OVERRIDE; 1.130 + NS_DISPLAY_DECL_NAME("ButtonBorderBackground", TYPE_BUTTON_BORDER_BACKGROUND) 1.131 +private: 1.132 + nsButtonFrameRenderer* mBFR; 1.133 +}; 1.134 + 1.135 +nsRect 1.136 +nsDisplayButtonBorderBackground::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) { 1.137 + *aSnap = false; 1.138 + return aBuilder->IsForEventDelivery() ? nsRect(ToReferenceFrame(), mFrame->GetSize()) 1.139 + : mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame(); 1.140 +} 1.141 + 1.142 +class nsDisplayButtonForeground : public nsDisplayItem { 1.143 +public: 1.144 + nsDisplayButtonForeground(nsDisplayListBuilder* aBuilder, 1.145 + nsButtonFrameRenderer* aRenderer) 1.146 + : nsDisplayItem(aBuilder, aRenderer->GetFrame()), mBFR(aRenderer) { 1.147 + MOZ_COUNT_CTOR(nsDisplayButtonForeground); 1.148 + } 1.149 +#ifdef NS_BUILD_REFCNT_LOGGING 1.150 + virtual ~nsDisplayButtonForeground() { 1.151 + MOZ_COUNT_DTOR(nsDisplayButtonForeground); 1.152 + } 1.153 +#endif 1.154 + 1.155 + virtual void Paint(nsDisplayListBuilder* aBuilder, 1.156 + nsRenderingContext* aCtx) MOZ_OVERRIDE; 1.157 + NS_DISPLAY_DECL_NAME("ButtonForeground", TYPE_BUTTON_FOREGROUND) 1.158 +private: 1.159 + nsButtonFrameRenderer* mBFR; 1.160 +}; 1.161 + 1.162 +void 1.163 +nsDisplayButtonBorderBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, 1.164 + const nsDisplayItemGeometry* aGeometry, 1.165 + nsRegion *aInvalidRegion) 1.166 +{ 1.167 + AddInvalidRegionForSyncDecodeBackgroundImages(aBuilder, aGeometry, aInvalidRegion); 1.168 + 1.169 + nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion); 1.170 +} 1.171 + 1.172 +void nsDisplayButtonBorderBackground::Paint(nsDisplayListBuilder* aBuilder, 1.173 + nsRenderingContext* aCtx) 1.174 +{ 1.175 + NS_ASSERTION(mFrame, "No frame?"); 1.176 + nsPresContext* pc = mFrame->PresContext(); 1.177 + nsRect r = nsRect(ToReferenceFrame(), mFrame->GetSize()); 1.178 + 1.179 + // draw the border and background inside the focus and outline borders 1.180 + mBFR->PaintBorderAndBackground(pc, *aCtx, mVisibleRect, r, 1.181 + aBuilder->GetBackgroundPaintFlags()); 1.182 +} 1.183 + 1.184 +void nsDisplayButtonForeground::Paint(nsDisplayListBuilder* aBuilder, 1.185 + nsRenderingContext* aCtx) 1.186 +{ 1.187 + nsPresContext *presContext = mFrame->PresContext(); 1.188 + const nsStyleDisplay *disp = mFrame->StyleDisplay(); 1.189 + if (!mFrame->IsThemed(disp) || 1.190 + !presContext->GetTheme()->ThemeDrawsFocusForWidget(disp->mAppearance)) { 1.191 + // draw the focus and outline borders 1.192 + nsRect r = nsRect(ToReferenceFrame(), mFrame->GetSize()); 1.193 + mBFR->PaintOutlineAndFocusBorders(presContext, *aCtx, mVisibleRect, r); 1.194 + } 1.195 +} 1.196 + 1.197 +nsresult 1.198 +nsButtonFrameRenderer::DisplayButton(nsDisplayListBuilder* aBuilder, 1.199 + nsDisplayList* aBackground, 1.200 + nsDisplayList* aForeground) 1.201 +{ 1.202 + if (mFrame->StyleBorder()->mBoxShadow) { 1.203 + aBackground->AppendNewToTop(new (aBuilder) 1.204 + nsDisplayButtonBoxShadowOuter(aBuilder, this)); 1.205 + } 1.206 + 1.207 + // Almost all buttons draw some kind of background so there's not much 1.208 + // point in checking whether we should create this item. 1.209 + aBackground->AppendNewToTop(new (aBuilder) 1.210 + nsDisplayButtonBorderBackground(aBuilder, this)); 1.211 + 1.212 + // Only display focus rings if we actually have them. Since at most one 1.213 + // button would normally display a focus ring, most buttons won't have them. 1.214 + if ((mOuterFocusStyle && mOuterFocusStyle->StyleBorder()->HasBorder()) || 1.215 + (mInnerFocusStyle && mInnerFocusStyle->StyleBorder()->HasBorder())) { 1.216 + aForeground->AppendNewToTop(new (aBuilder) 1.217 + nsDisplayButtonForeground(aBuilder, this)); 1.218 + } 1.219 + return NS_OK; 1.220 +} 1.221 + 1.222 +void 1.223 +nsButtonFrameRenderer::PaintOutlineAndFocusBorders(nsPresContext* aPresContext, 1.224 + nsRenderingContext& aRenderingContext, 1.225 + const nsRect& aDirtyRect, 1.226 + const nsRect& aRect) 1.227 +{ 1.228 + // once we have all that we'll draw the focus if we have it. We will 1.229 + // need to draw 2 focuses, the inner and the outer. This is so we 1.230 + // can do any kind of look and feel. Some buttons have focus on the 1.231 + // outside like mac and motif. While others like windows have it 1.232 + // inside (dotted line). Usually only one will be specifed. But I 1.233 + // guess you could have both if you wanted to. 1.234 + 1.235 + nsRect rect; 1.236 + 1.237 + if (mOuterFocusStyle) { 1.238 + // ---------- paint the outer focus border ------------- 1.239 + 1.240 + GetButtonOuterFocusRect(aRect, rect); 1.241 + 1.242 + nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, mFrame, 1.243 + aDirtyRect, rect, mOuterFocusStyle); 1.244 + } 1.245 + 1.246 + if (mInnerFocusStyle) { 1.247 + // ---------- paint the inner focus border ------------- 1.248 + 1.249 + GetButtonInnerFocusRect(aRect, rect); 1.250 + 1.251 + nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, mFrame, 1.252 + aDirtyRect, rect, mInnerFocusStyle); 1.253 + } 1.254 +} 1.255 + 1.256 + 1.257 +void 1.258 +nsButtonFrameRenderer::PaintBorderAndBackground(nsPresContext* aPresContext, 1.259 + nsRenderingContext& aRenderingContext, 1.260 + const nsRect& aDirtyRect, 1.261 + const nsRect& aRect, 1.262 + uint32_t aBGFlags) 1.263 + 1.264 +{ 1.265 + // get the button rect this is inside the focus and outline rects 1.266 + nsRect buttonRect; 1.267 + GetButtonRect(aRect, buttonRect); 1.268 + 1.269 + nsStyleContext* context = mFrame->StyleContext(); 1.270 + 1.271 + nsCSSRendering::PaintBackground(aPresContext, aRenderingContext, mFrame, 1.272 + aDirtyRect, buttonRect, aBGFlags); 1.273 + nsCSSRendering::PaintBoxShadowInner(aPresContext, aRenderingContext, 1.274 + mFrame, buttonRect, aDirtyRect); 1.275 + nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, mFrame, 1.276 + aDirtyRect, buttonRect, context); 1.277 +} 1.278 + 1.279 + 1.280 +void 1.281 +nsButtonFrameRenderer::GetButtonOuterFocusRect(const nsRect& aRect, nsRect& focusRect) 1.282 +{ 1.283 + focusRect = aRect; 1.284 +} 1.285 + 1.286 +void 1.287 +nsButtonFrameRenderer::GetButtonRect(const nsRect& aRect, nsRect& r) 1.288 +{ 1.289 + r = aRect; 1.290 + r.Deflate(GetButtonOuterFocusBorderAndPadding()); 1.291 +} 1.292 + 1.293 + 1.294 +void 1.295 +nsButtonFrameRenderer::GetButtonInnerFocusRect(const nsRect& aRect, nsRect& focusRect) 1.296 +{ 1.297 + GetButtonRect(aRect, focusRect); 1.298 + focusRect.Deflate(GetButtonBorderAndPadding()); 1.299 + focusRect.Deflate(GetButtonInnerFocusMargin()); 1.300 +} 1.301 + 1.302 + 1.303 +nsMargin 1.304 +nsButtonFrameRenderer::GetButtonOuterFocusBorderAndPadding() 1.305 +{ 1.306 + nsMargin result(0,0,0,0); 1.307 + 1.308 + if (mOuterFocusStyle) { 1.309 + if (!mOuterFocusStyle->StylePadding()->GetPadding(result)) { 1.310 + NS_NOTYETIMPLEMENTED("percentage padding"); 1.311 + } 1.312 + result += mOuterFocusStyle->StyleBorder()->GetComputedBorder(); 1.313 + } 1.314 + 1.315 + return result; 1.316 +} 1.317 + 1.318 +nsMargin 1.319 +nsButtonFrameRenderer::GetButtonBorderAndPadding() 1.320 +{ 1.321 + return mFrame->GetUsedBorderAndPadding(); 1.322 +} 1.323 + 1.324 +/** 1.325 + * Gets the size of the buttons border this is the union of the normal and disabled borders. 1.326 + */ 1.327 +nsMargin 1.328 +nsButtonFrameRenderer::GetButtonInnerFocusMargin() 1.329 +{ 1.330 + nsMargin innerFocusMargin(0,0,0,0); 1.331 + 1.332 + if (mInnerFocusStyle) { 1.333 + const nsStyleMargin* margin = mInnerFocusStyle->StyleMargin(); 1.334 + if (!margin->GetMargin(innerFocusMargin)) { 1.335 + NS_NOTYETIMPLEMENTED("percentage margin"); 1.336 + } 1.337 + } 1.338 + 1.339 + return innerFocusMargin; 1.340 +} 1.341 + 1.342 +nsMargin 1.343 +nsButtonFrameRenderer::GetButtonInnerFocusBorderAndPadding() 1.344 +{ 1.345 + nsMargin result(0,0,0,0); 1.346 + 1.347 + if (mInnerFocusStyle) { 1.348 + if (!mInnerFocusStyle->StylePadding()->GetPadding(result)) { 1.349 + NS_NOTYETIMPLEMENTED("percentage padding"); 1.350 + } 1.351 + result += mInnerFocusStyle->StyleBorder()->GetComputedBorder(); 1.352 + } 1.353 + 1.354 + return result; 1.355 +} 1.356 + 1.357 +// gets all the focus borders and padding that will be added to the regular border 1.358 +nsMargin 1.359 +nsButtonFrameRenderer::GetAddedButtonBorderAndPadding() 1.360 +{ 1.361 + return GetButtonOuterFocusBorderAndPadding() + GetButtonInnerFocusMargin() + GetButtonInnerFocusBorderAndPadding(); 1.362 +} 1.363 + 1.364 +/** 1.365 + * Call this when styles change 1.366 + */ 1.367 +void 1.368 +nsButtonFrameRenderer::ReResolveStyles(nsPresContext* aPresContext) 1.369 +{ 1.370 + // get all the styles 1.371 + nsStyleContext* context = mFrame->StyleContext(); 1.372 + nsStyleSet *styleSet = aPresContext->StyleSet(); 1.373 + 1.374 + // style for the inner such as a dotted line (Windows) 1.375 + mInnerFocusStyle = 1.376 + styleSet->ProbePseudoElementStyle(mFrame->GetContent()->AsElement(), 1.377 + nsCSSPseudoElements::ePseudo_mozFocusInner, 1.378 + context); 1.379 + 1.380 + // style for outer focus like a ridged border (MAC). 1.381 + mOuterFocusStyle = 1.382 + styleSet->ProbePseudoElementStyle(mFrame->GetContent()->AsElement(), 1.383 + nsCSSPseudoElements::ePseudo_mozFocusOuter, 1.384 + context); 1.385 +} 1.386 + 1.387 +nsStyleContext* 1.388 +nsButtonFrameRenderer::GetStyleContext(int32_t aIndex) const 1.389 +{ 1.390 + switch (aIndex) { 1.391 + case NS_BUTTON_RENDERER_FOCUS_INNER_CONTEXT_INDEX: 1.392 + return mInnerFocusStyle; 1.393 + case NS_BUTTON_RENDERER_FOCUS_OUTER_CONTEXT_INDEX: 1.394 + return mOuterFocusStyle; 1.395 + default: 1.396 + return nullptr; 1.397 + } 1.398 +} 1.399 + 1.400 +void 1.401 +nsButtonFrameRenderer::SetStyleContext(int32_t aIndex, nsStyleContext* aStyleContext) 1.402 +{ 1.403 + switch (aIndex) { 1.404 + case NS_BUTTON_RENDERER_FOCUS_INNER_CONTEXT_INDEX: 1.405 + mInnerFocusStyle = aStyleContext; 1.406 + break; 1.407 + case NS_BUTTON_RENDERER_FOCUS_OUTER_CONTEXT_INDEX: 1.408 + mOuterFocusStyle = aStyleContext; 1.409 + break; 1.410 + } 1.411 +}