Last Comment Bug 307502 - absolutely positioned child has negative offsetTop/Left when parent has border, overflow:hidden, position:relative
: absolutely positioned child has negative offsetTop/Left when parent has borde...
spec needed
: regression, testcase
Product: Core
Classification: Components
Component: DOM (show other bugs)
: 1.8 Branch
: x86 Windows XP
-- normal with 1 vote (vote)
: ---
Assigned To: Nobody; OK to take it and work on it
: Andrew Overholt [:overholt]
Depends on:
Blocks: 353363
  Show dependency treegraph
Reported: 2005-09-08 07:39 PDT by jbrunette
Modified: 2016-07-18 10:19 PDT (History)
18 users (show)
dbaron: blocking1.9-
reed: wanted1.9+
darin.moz: blocking1.8.1-
dveditz: blocking1.8.0.1-
See Also:
Crash Signature:
QA Whiteboard:
Iteration: ---
Points: ---
Has Regression Range: ---
Has STR: ---

testcase illustrating the problem (564 bytes, text/html)
2005-09-08 07:40 PDT, jbrunette
no flags Details
MSIE on testcase (26.03 KB, image/png)
2005-12-29 09:05 PST, Chris Thomas (CTho) [formerly]
no flags Details
Div sizing/positioning test page set to show negative offetLeft problem (18.36 KB, text/html)
2009-09-30 06:17 PDT, Bob F
no flags Details
offset.html (595 bytes, text/html)
2016-07-18 09:59 PDT, Michelle Funches - QA
no flags Details

Description User image jbrunette 2005-09-08 07:39:00 PDT
User-Agent:       Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8b4) Gecko/20050907 Firefox/1.4
Build Identifier: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8b4) Gecko/20050907 Firefox/1.4

Firefox 1.5 beta1 release candidate build.

If a parent element has a border, overflow:hidden and position:relative, a child
element's offsetLeft and offsetTop could be negative.

Reproducible: Always

Steps to Reproduce:
1. View attached testcase

Actual Results:  
test's offsetTop = -100
test's offsetLeft = -100

Expected Results:  
test's offsetTop = 0
test's offsetLeft = 0
Comment 1 User image jbrunette 2005-09-08 07:40:24 PDT
Created attachment 195268 [details]
testcase illustrating the problem
Comment 2 User image Adam Guthrie 2005-09-08 12:02:30 PDT
Duplicate of bug 255754?
Comment 3 User image Zook Valem 2005-09-08 18:14:46 PDT
I tested the testcase with and without "<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML
4.01 Transitional//EN">) to be on the safe side.

Out of curiousity, I removed EITHER the "overflow: hidden;" OR "position:
relative" to see how this testcase react.  What I found instead is that
offsetTop and offsetLeft did return to a value of zero.  It's an unexpected
result.  Maybe, it is not be a dup of bug 255754.
Comment 4 User image Gérard Talbot 2005-09-08 21:09:53 PDT
Opera 8.02 also renders offsetLeft, offsetTop differently depending on position
and overflow values. 
The problem to begin with, the origin of the problem is that MSDN does not
define strictly, explicitly how offsetLeft/offsetTop should be computed in
normal situations and in various other situations. Even offsetParent is not
defined clearly: there is no precise spec. - formal rigorous spec - identifying
how the offsetParent can be, should be determined. offsetLeft, offsetTop
definitions are circularly dependent, self-inter-dependent to offsetParent
Add to this quirks mode vs standards rendering mode, and other properties
(padding, border, margin, box-sizing) which  interfere or affect too
calculations of offsetLeft, offsetTop or which may influence too the way
offsetParent is to be defined and then you have a real mess, headache,
impossible task.

This bug is entirely up to module owner.
Comment 5 User image Gérard Talbot 2005-09-08 21:19:41 PDT
Also, since these properties (offsetLeft/Top/Parent) come from Microsoft DHTML
object model, when trying to implement such properties as correctly as one can,
we also become chained, dependent on how Microsoft defines and implements those
properties. Who knows if in 3 months these properties will be re-defined
differently and implemented differently in IE 7... 

-> Component: DOM: Mozilla Extensions
Comment 6 User image Boris Zbarsky [:bz] (still a bit busy) 2005-12-28 18:32:28 PST
So what's IE's behavior here?
Comment 7 User image Martijn Wargers [:mwargers] 2005-12-29 05:22:41 PST
IE6 results:
test's offsetTop is 100
test's offsetLeft is 100

Mozilla1.7 results:
test's offsetTop is 0
test's offsetLeft is 0

Current build results:
test's offsetTop is -100
test's offsetLeft is -100

It might be useful to know when this behavior changed in Mozilla (Ria?).
Comment 9 User image Boris Zbarsky [:bz] (still a bit busy) 2005-12-29 08:10:13 PST
Looks like the scrollframe changes to not be a box frame changed something that the DOM was relying on...  This breaks the use of offsetTop/Left for this relatively common case, so we should really try to get this fixed on the branches too.  :(
Comment 10 User image Chris Thomas (CTho) [formerly] 2005-12-29 09:05:22 PST
Created attachment 207087 [details]
MSIE on testcase
Comment 11 User image Boris Zbarsky [:bz] (still a bit busy) 2005-12-29 09:14:04 PST
Note that for MSIE what I'm more interested in is not just behavior on this testcase but behavior in general.  What does offset* actually do?
Comment 12 User image Robert O'Callahan (:roc) (email my personal email if necessary) 2005-12-29 12:32:14 PST
What a mess. Maybe Ian can write a spec for this? (or maybe he already has? hope hope hope)
Comment 13 User image Robert O'Callahan (:roc) (email my personal email if necessary) 2005-12-29 12:32:55 PST
I'd really like to have a spec before messing with that code.
Comment 14 User image Boris Zbarsky [:bz] (still a bit busy) 2005-12-29 13:28:37 PST
Agreed; hence the questions about what IE does.
Comment 15 User image Daniel Veditz [:dveditz] 2006-01-04 09:43:45 PST
If we're still trying to write a spec it doesn't sound like this could possibly make  If you get a safe, tested patch checked into the trunk you can request approval on it.
Comment 16 User image Anne (:annevk) 2006-06-14 00:14:17 PDT

According to the specification we should not do the border quirks from Internet Explorer and just calculate from border to border in case there is an offsetParent and otherwise from border to the initial containing block. This is what Opera has implemented at the moment.
Comment 17 User image Boris Zbarsky [:bz] (still a bit busy) 2006-06-14 07:31:09 PDT
Does that give sufficient compat with IE that the properties are usable in these situations?
Comment 18 User image Anne (:annevk) 2006-06-14 13:04:50 PDT
Yeah. I'd say more usable as you never have to add the border yourself.

The only real quirk you have to take over from Internet Explorer (as said somewhere in another bug) is having document.body (wrongly indicated as HTMLBodyElement in the specification) as final offsetParent which means that document.body will always have an offsetLeft and offsetTop of 0 and when document.body is the offsetParent you calculate things against the initial containing block instead of against document.body.

(The difference between document.body and HTMLBodyElement is strictly that multiple nodes can implement the HTMLBodyElement interface, but there's only one document.body ever.)
Comment 19 User image Boris Zbarsky [:bz] (still a bit busy) 2006-06-14 15:22:34 PDT
document.body can be an HTMLFramesetElement, for what it's worth.  What happens then?
Comment 20 User image Anne (:annevk) 2006-06-14 22:55:01 PDT
Yeah, I haven't worked out frames yet (see open issues). Hope to get to that before July, but there are other things I have to do as well...
Comment 21 User image Darin Fisher 2006-06-16 18:09:33 PDT
Not going to block 1.8.1 for this bug, but we'll happily consider a baked-on-trunk patch.
Comment 22 User image Anne (:annevk) 2006-06-17 11:37:20 PDT
So in Internet Explorer every <frameset> element is part of the offsetParent chain. The offsetParent of the <frameset> element that matches document.body is <html> and not null, surprisingly enough. I guess it makes sense to follow that, except perhaps for the fact that here document.body.offsetParent is <html> instead of null... I'd rather be consistent.
Comment 23 User image Topper 2006-10-14 07:47:58 PDT
I think this is more general problem. If a parent element's css overflow property value is not visible(i.e. auto,hidden or scroll), then the offsetLeft and offsetTop values of child element will be wrong. The result offsetTop value happens to be the correct value substracts the border-top-width of parent element. The result offsetLeft value happens to be the correct value substracts the border-left-width of parent element. I think this is due to BoxObject bug. please check bug#353363 and bug#356066. 

Comment 24 User image Boris Zbarsky [:bz] (still a bit busy) 2006-10-15 09:02:42 PDT
offset* does not use box objects.  So bug 356066 is not relevant.

Again, what this bug needs is a spec.
Comment 25 User image Eli Friedman 2007-05-16 21:45:02 PDT
The negative offset bug is gone on trunk.

I think the issue here is that what IE does here is a bit insane.  Apparently, we're supposed to subtract out the border for absolutely positioned elements, but not for relatively positioned ones.
Comment 26 User image Bob F 2009-09-30 06:17:25 PDT
Created attachment 403764 [details]
Div sizing/positioning test page set to show negative offetLeft problem

This page shows a negative offsetTop/Left value (on the bottom line of text), for the innermost nested div (id='ccDiv' border color blue).

If you change ccDiv to normal position-ing by pressing the '()' button on the blue line, or change the offsetParent (aaDiv, the upper red line) so that it has style.overflow visible (press the 'visible') button, the offset value (shown on the bottom line, will not be negative.
Comment 27 User image Bob F 2009-09-30 06:23:34 PDT
I tracked the cause of this bug to nsGenericHTMLElement.cpp GetOffsetRect().  In the version of source I was using (3.5), on line 572 there is the following code:

  // Subtract the parent border unless it uses border-box sizing.
  if (parent &&
      parent->GetStylePosition()->mBoxSizing != NS_STYLE_BOX_SIZING_BORDER) {
    const nsStyleBorder* border = parent->GetStyleBorder();
    origin.x -= border->GetActualBorderWidth(NS_SIDE_LEFT);
    origin.y -= border->GetActualBorderWidth(NS_SIDE_TOP);

In a test using the attachment above (403764) pressing the 'Test cc offsetLeft' button invokes GetOffsetLeft which calls GetOffsetRect.  In that code, parent->GetStylePosition()->mBoxSizing evaluates to 0 which causes the body of the 'if' above to execute.  This is what causes the offset to be (incorrectly) negative.

I don't understand what the purpose of this 'if' statement is.
Comment 28 User image Boris Zbarsky [:bz] (still a bit busy) 2009-09-30 08:01:16 PDT
The purpose is to make sure coordinates end up in the right coordinate system (specifically, the same one that "width" is measured in).  But note comment 13 and comment 16.  Is there now a proposed spec that's web-compatible?

In any case, what's happening here is that in GetOffsetRect our primary frame is abs pos.  Its parent is the scrolled frame, whose parent is the scrollframe.  The nonzero border is on the scrollframe.  The scrollframe is relatively positioned.

isPositioned and isAbsolutelyPositioned are both true.  Therefore isOffsetParent ends up false, but this block:

499           if (!isAbsolutelyPositioned && !isOffsetParent) {
500             origin += parent->GetPositionIgnoringScrolling();
501           }

is not executed when |parent| is the scrolled frame.  So we end up not adding its origin (which is precisely offset by the border width).  We continue in the loop with the scrollframe, which is treated as the offsetparent, and we break out of the loop and subtract the border widths, which we never added.

It looks like this code was added in bug 81290.  jst, I bet you don't recall why you added it, right?

It seems to me that no matter what else we do here that code is wrong: the parent of an abs pos node is its containing block in all cases, so if that containing block is offset from the offsetParent primary frame (the positioned one) for some reason we should be including that offset...  As in, we should just remove the above code block.
Comment 29 User image Ioana (away) 2013-12-13 00:24:22 PST
Removing keyword since the request was answered in comment 7.
Comment 30 User image Michelle Funches - QA 2016-07-15 14:37:56 PDT
Version 	47.0.1
Build ID 	20160623154057
User Agent 	Mozilla/5.0 (Windows NT 10.0; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0
User Agent 	Mozilla/5.0 (Windows NT 5.1; rv:50.0) Gecko/20100101 Firefox/50.0
Comment 31 User image Boris Zbarsky [:bz] (still a bit busy) 2016-07-15 20:27:35 PDT
Michelle, what works for you?  The bug is clearly reproducible using attachment 403764 [details].

Attachment 195268 [details] doesn't show a problem, but it hasn't since comment 25 at least.
Comment 32 User image Michelle Funches - QA 2016-07-18 09:59:11 PDT
Created attachment 8772040 [details]

Attached file for what I tested (offset.html)
Comment 33 User image Boris Zbarsky [:bz] (still a bit busy) 2016-07-18 10:18:40 PDT
Yes, that's the attachment that hasn't shown this problem for 9 years now and that I marked obsolete 3 days ago...  The _other_ testcase on this bug shows the problem just fine.

Note You need to log in before you can comment on or make changes to this bug.