2003-01-05 08:05:17 +03:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
2004-04-18 18:30:37 +04:00
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
2003-01-05 08:05:17 +03:00
|
|
|
*
|
2004-04-18 18:30:37 +04:00
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
2003-01-05 08:05:17 +03:00
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is mozilla.org code.
|
|
|
|
*
|
2004-04-18 18:30:37 +04:00
|
|
|
* The Initial Developer of the Original Code is
|
|
|
|
* Netscape Communications Corporation.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 1998
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
2003-01-05 08:05:17 +03:00
|
|
|
*
|
2004-04-18 18:30:37 +04:00
|
|
|
* Contributor(s):
|
2003-01-05 08:05:17 +03:00
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
2004-04-18 18:30:37 +04:00
|
|
|
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
|
|
|
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
2003-01-05 08:05:17 +03:00
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
2004-04-18 18:30:37 +04:00
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
2003-01-05 08:05:17 +03:00
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
2004-04-18 18:30:37 +04:00
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
2003-01-05 08:05:17 +03:00
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
|
|
|
#include "nsLayoutUtils.h"
|
|
|
|
#include "nsIFrame.h"
|
2005-12-13 02:53:06 +03:00
|
|
|
#include "nsIFormControlFrame.h"
|
2004-08-01 03:15:21 +04:00
|
|
|
#include "nsPresContext.h"
|
2003-01-05 08:05:17 +03:00
|
|
|
#include "nsIContent.h"
|
|
|
|
#include "nsFrameList.h"
|
2003-02-23 05:49:22 +03:00
|
|
|
#include "nsLayoutAtoms.h"
|
2003-04-22 03:06:40 +04:00
|
|
|
#include "nsIAtom.h"
|
|
|
|
#include "nsCSSPseudoElements.h"
|
2003-10-15 04:49:03 +04:00
|
|
|
#include "nsIView.h"
|
2004-08-06 19:55:17 +04:00
|
|
|
#include "nsIScrollableView.h"
|
2004-09-13 17:35:46 +04:00
|
|
|
#include "nsPlaceholderFrame.h"
|
2004-09-21 08:41:08 +04:00
|
|
|
#include "nsIScrollableFrame.h"
|
2005-02-08 05:15:26 +03:00
|
|
|
#include "nsCSSFrameConstructor.h"
|
2005-06-02 13:00:48 +04:00
|
|
|
#include "nsIPrivateDOMEvent.h"
|
|
|
|
#include "nsIDOMEvent.h"
|
|
|
|
#include "nsGUIEvent.h"
|
2003-01-05 08:05:17 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* A namespace class for static layout utilities.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* GetFirstChildFrame returns the first "real" child frame of a
|
|
|
|
* given frame. It will descend down into pseudo-frames (unless the
|
|
|
|
* pseudo-frame is the :before generated frame).
|
|
|
|
* @param aFrame the frame
|
|
|
|
* @param aFrame the frame's content node
|
|
|
|
*/
|
|
|
|
static nsIFrame*
|
2005-02-18 09:06:47 +03:00
|
|
|
GetFirstChildFrame(nsIFrame* aFrame,
|
2003-01-05 08:05:17 +03:00
|
|
|
nsIContent* aContent)
|
|
|
|
{
|
|
|
|
NS_PRECONDITION(aFrame, "NULL frame pointer");
|
|
|
|
|
|
|
|
// Get the first child frame
|
2004-01-09 17:20:53 +03:00
|
|
|
nsIFrame* childFrame = aFrame->GetFirstChild(nsnull);
|
2003-01-05 08:05:17 +03:00
|
|
|
|
|
|
|
// If the child frame is a pseudo-frame, then return its first child.
|
|
|
|
// Note that the frame we create for the generated content is also a
|
|
|
|
// pseudo-frame and so don't drill down in that case
|
|
|
|
if (childFrame &&
|
|
|
|
childFrame->IsPseudoFrame(aContent) &&
|
|
|
|
!childFrame->IsGeneratedContentFrame()) {
|
2005-02-18 09:06:47 +03:00
|
|
|
return GetFirstChildFrame(childFrame, aContent);
|
2003-01-05 08:05:17 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return childFrame;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* GetLastChildFrame returns the last "real" child frame of a
|
|
|
|
* given frame. It will descend down into pseudo-frames (unless the
|
|
|
|
* pseudo-frame is the :after generated frame).
|
|
|
|
* @param aFrame the frame
|
|
|
|
* @param aFrame the frame's content node
|
|
|
|
*/
|
|
|
|
static nsIFrame*
|
2005-02-18 09:06:47 +03:00
|
|
|
GetLastChildFrame(nsIFrame* aFrame,
|
2003-01-05 08:05:17 +03:00
|
|
|
nsIContent* aContent)
|
|
|
|
{
|
|
|
|
NS_PRECONDITION(aFrame, "NULL frame pointer");
|
|
|
|
|
|
|
|
// Get the last in flow frame
|
2003-01-16 05:59:04 +03:00
|
|
|
nsIFrame* lastInFlow = aFrame->GetLastInFlow();
|
2003-01-05 08:05:17 +03:00
|
|
|
|
|
|
|
// Get the last child frame
|
2004-01-09 17:20:53 +03:00
|
|
|
nsIFrame* firstChildFrame = lastInFlow->GetFirstChild(nsnull);
|
2003-01-05 08:05:17 +03:00
|
|
|
if (firstChildFrame) {
|
|
|
|
nsFrameList frameList(firstChildFrame);
|
|
|
|
nsIFrame* lastChildFrame = frameList.LastChild();
|
|
|
|
|
|
|
|
NS_ASSERTION(lastChildFrame, "unexpected error");
|
|
|
|
|
|
|
|
// Get the frame's first-in-flow. This matters in case the frame has
|
|
|
|
// been continuted across multiple lines
|
2003-01-16 05:59:04 +03:00
|
|
|
lastChildFrame = lastChildFrame->GetFirstInFlow();
|
|
|
|
|
2003-01-05 08:05:17 +03:00
|
|
|
// If the last child frame is a pseudo-frame, then return its last child.
|
|
|
|
// Note that the frame we create for the generated content is also a
|
|
|
|
// pseudo-frame and so don't drill down in that case
|
|
|
|
if (lastChildFrame &&
|
|
|
|
lastChildFrame->IsPseudoFrame(aContent) &&
|
|
|
|
!lastChildFrame->IsGeneratedContentFrame()) {
|
2005-02-18 09:06:47 +03:00
|
|
|
return GetLastChildFrame(lastChildFrame, aContent);
|
2003-01-05 08:05:17 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return lastChildFrame;
|
|
|
|
}
|
|
|
|
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
// static
|
|
|
|
nsIFrame*
|
2005-02-18 09:06:47 +03:00
|
|
|
nsLayoutUtils::GetBeforeFrame(nsIFrame* aFrame)
|
2003-01-05 08:05:17 +03:00
|
|
|
{
|
|
|
|
NS_PRECONDITION(aFrame, "NULL frame pointer");
|
2004-09-14 06:28:03 +04:00
|
|
|
NS_ASSERTION(!aFrame->GetPrevInFlow(), "aFrame must be first-in-flow");
|
2003-01-05 08:05:17 +03:00
|
|
|
|
2005-02-18 09:06:47 +03:00
|
|
|
nsIFrame* firstFrame = GetFirstChildFrame(aFrame, aFrame->GetContent());
|
2003-01-05 08:05:17 +03:00
|
|
|
|
2003-04-22 03:06:40 +04:00
|
|
|
if (firstFrame && IsGeneratedContentFor(nsnull, firstFrame,
|
|
|
|
nsCSSPseudoElements::before)) {
|
2003-01-05 08:05:17 +03:00
|
|
|
return firstFrame;
|
|
|
|
}
|
|
|
|
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
// static
|
|
|
|
nsIFrame*
|
2005-02-18 09:06:47 +03:00
|
|
|
nsLayoutUtils::GetAfterFrame(nsIFrame* aFrame)
|
2003-01-05 08:05:17 +03:00
|
|
|
{
|
|
|
|
NS_PRECONDITION(aFrame, "NULL frame pointer");
|
|
|
|
|
2005-02-18 09:06:47 +03:00
|
|
|
nsIFrame* lastFrame = GetLastChildFrame(aFrame, aFrame->GetContent());
|
2003-01-05 08:05:17 +03:00
|
|
|
|
2003-04-22 03:06:40 +04:00
|
|
|
if (lastFrame && IsGeneratedContentFor(nsnull, lastFrame,
|
|
|
|
nsCSSPseudoElements::after)) {
|
2003-01-05 08:05:17 +03:00
|
|
|
return lastFrame;
|
|
|
|
}
|
|
|
|
|
|
|
|
return nsnull;
|
|
|
|
}
|
2003-02-23 05:49:22 +03:00
|
|
|
|
2003-04-22 03:06:40 +04:00
|
|
|
// static
|
2003-02-23 05:49:22 +03:00
|
|
|
nsIFrame*
|
|
|
|
nsLayoutUtils::GetPageFrame(nsIFrame* aFrame)
|
|
|
|
{
|
2003-07-02 14:30:00 +04:00
|
|
|
for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) {
|
2003-10-31 23:19:18 +03:00
|
|
|
if (frame->GetType() == nsLayoutAtoms::pageFrame) {
|
2003-02-23 05:49:22 +03:00
|
|
|
return frame;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nsnull;
|
|
|
|
}
|
2003-04-22 03:06:40 +04:00
|
|
|
|
2004-09-13 17:35:46 +04:00
|
|
|
nsIFrame*
|
|
|
|
nsLayoutUtils::GetFloatFromPlaceholder(nsIFrame* aFrame) {
|
|
|
|
if (nsLayoutAtoms::placeholderFrame != aFrame->GetType()) {
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIFrame *outOfFlowFrame =
|
2005-04-14 19:30:35 +04:00
|
|
|
nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame);
|
|
|
|
if (outOfFlowFrame->GetStyleDisplay()->IsFloating()) {
|
2004-09-13 17:35:46 +04:00
|
|
|
return outOfFlowFrame;
|
|
|
|
}
|
|
|
|
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
|
2003-04-22 03:06:40 +04:00
|
|
|
// static
|
|
|
|
PRBool
|
|
|
|
nsLayoutUtils::IsGeneratedContentFor(nsIContent* aContent,
|
|
|
|
nsIFrame* aFrame,
|
|
|
|
nsIAtom* aPseudoElement)
|
|
|
|
{
|
|
|
|
NS_PRECONDITION(aFrame, "Must have a frame");
|
|
|
|
NS_PRECONDITION(aPseudoElement, "Must have a pseudo name");
|
|
|
|
|
|
|
|
if (!aFrame->IsGeneratedContentFrame()) {
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
2003-10-31 23:19:18 +03:00
|
|
|
|
2003-07-02 14:30:00 +04:00
|
|
|
if (aContent && aFrame->GetContent() != aContent) {
|
|
|
|
return PR_FALSE;
|
2003-04-22 03:06:40 +04:00
|
|
|
}
|
|
|
|
|
2003-10-17 06:38:37 +04:00
|
|
|
return aFrame->GetStyleContext()->GetPseudoType() == aPseudoElement;
|
2003-04-22 03:06:40 +04:00
|
|
|
}
|
|
|
|
|
2003-10-15 04:49:03 +04:00
|
|
|
// static
|
|
|
|
PRBool
|
|
|
|
nsLayoutUtils::IsProperAncestorFrame(nsIFrame* aAncestorFrame, nsIFrame* aFrame,
|
|
|
|
nsIFrame* aCommonAncestor)
|
|
|
|
{
|
|
|
|
if (aFrame == aCommonAncestor) {
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIFrame* parentFrame = aFrame->GetParent();
|
|
|
|
|
|
|
|
while (parentFrame != aCommonAncestor) {
|
|
|
|
if (parentFrame == aAncestorFrame) {
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
parentFrame = parentFrame->GetParent();
|
|
|
|
}
|
|
|
|
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// static
|
|
|
|
PRInt32
|
2004-04-13 01:53:22 +04:00
|
|
|
nsLayoutUtils::DoCompareTreePosition(nsIContent* aContent1,
|
|
|
|
nsIContent* aContent2,
|
|
|
|
PRInt32 aIf1Ancestor,
|
|
|
|
PRInt32 aIf2Ancestor,
|
|
|
|
nsIContent* aCommonAncestor)
|
|
|
|
{
|
2003-10-15 04:49:03 +04:00
|
|
|
NS_PRECONDITION(aContent1, "aContent1 must not be null");
|
|
|
|
NS_PRECONDITION(aContent2, "aContent2 must not be null");
|
|
|
|
|
|
|
|
nsAutoVoidArray content1Ancestors;
|
2003-10-17 18:10:02 +04:00
|
|
|
nsIContent* c1;
|
|
|
|
for (c1 = aContent1; c1 && c1 != aCommonAncestor; c1 = c1->GetParent()) {
|
|
|
|
content1Ancestors.AppendElement(c1);
|
|
|
|
}
|
|
|
|
if (!c1 && aCommonAncestor) {
|
|
|
|
// So, it turns out aCommonAncestor was not an ancestor of c1. Oops.
|
|
|
|
// Never mind. We can continue as if aCommonAncestor was null.
|
|
|
|
aCommonAncestor = nsnull;
|
|
|
|
}
|
2003-10-15 04:49:03 +04:00
|
|
|
|
2003-10-17 18:10:02 +04:00
|
|
|
nsAutoVoidArray content2Ancestors;
|
|
|
|
nsIContent* c2;
|
|
|
|
for (c2 = aContent2; c2 && c2 != aCommonAncestor; c2 = c2->GetParent()) {
|
|
|
|
content2Ancestors.AppendElement(c2);
|
2003-10-15 04:49:03 +04:00
|
|
|
}
|
2003-10-17 18:10:02 +04:00
|
|
|
if (!c2 && aCommonAncestor) {
|
|
|
|
// So, it turns out aCommonAncestor was not an ancestor of c2.
|
|
|
|
// We need to retry with no common ancestor hint.
|
2004-04-13 01:53:22 +04:00
|
|
|
return DoCompareTreePosition(aContent1, aContent2,
|
|
|
|
aIf1Ancestor, aIf2Ancestor, nsnull);
|
2003-10-15 04:49:03 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
int last1 = content1Ancestors.Count() - 1;
|
|
|
|
int last2 = content2Ancestors.Count() - 1;
|
|
|
|
nsIContent* content1Ancestor = nsnull;
|
|
|
|
nsIContent* content2Ancestor = nsnull;
|
|
|
|
while (last1 >= 0 && last2 >= 0
|
|
|
|
&& ((content1Ancestor = NS_STATIC_CAST(nsIContent*, content1Ancestors.ElementAt(last1)))
|
|
|
|
== (content2Ancestor = NS_STATIC_CAST(nsIContent*, content2Ancestors.ElementAt(last2))))) {
|
|
|
|
last1--;
|
|
|
|
last2--;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (last1 < 0) {
|
|
|
|
if (last2 < 0) {
|
|
|
|
NS_ASSERTION(aContent1 == aContent2, "internal error?");
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
// aContent1 is an ancestor of aContent2
|
2004-04-13 01:53:22 +04:00
|
|
|
return aIf1Ancestor;
|
2003-10-15 04:49:03 +04:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (last2 < 0) {
|
|
|
|
// aContent2 is an ancestor of aContent1
|
2004-04-13 01:53:22 +04:00
|
|
|
return aIf2Ancestor;
|
2003-10-15 04:49:03 +04:00
|
|
|
} else {
|
|
|
|
// content1Ancestor != content2Ancestor, so they must be siblings with the same parent
|
|
|
|
nsIContent* parent = content1Ancestor->GetParent();
|
|
|
|
NS_ASSERTION(parent, "no common ancestor at all???");
|
|
|
|
if (!parent) { // different documents??
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRInt32 index1 = parent->IndexOf(content1Ancestor);
|
|
|
|
PRInt32 index2 = parent->IndexOf(content2Ancestor);
|
|
|
|
if (index1 < 0 || index2 < 0) {
|
|
|
|
// one of them must be anonymous; we can't determine the order
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return index1 - index2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// static
|
|
|
|
nsIFrame* nsLayoutUtils::GetLastSibling(nsIFrame* aFrame) {
|
|
|
|
if (!aFrame) {
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIFrame* next;
|
|
|
|
while ((next = aFrame->GetNextSibling()) != nsnull) {
|
|
|
|
aFrame = next;
|
|
|
|
}
|
|
|
|
return aFrame;
|
|
|
|
}
|
|
|
|
|
|
|
|
// static
|
|
|
|
nsIView*
|
|
|
|
nsLayoutUtils::FindSiblingViewFor(nsIView* aParentView, nsIFrame* aFrame) {
|
|
|
|
nsIFrame* parentViewFrame = NS_STATIC_CAST(nsIFrame*, aParentView->GetClientData());
|
|
|
|
nsIContent* parentViewContent = parentViewFrame ? parentViewFrame->GetContent() : nsnull;
|
|
|
|
for (nsIView* insertBefore = aParentView->GetFirstChild(); insertBefore;
|
|
|
|
insertBefore = insertBefore->GetNextSibling()) {
|
|
|
|
nsIFrame* f = NS_STATIC_CAST(nsIFrame*, insertBefore->GetClientData());
|
|
|
|
if (!f) {
|
|
|
|
// this view could be some anonymous view attached to a meaningful parent
|
|
|
|
for (nsIView* searchView = insertBefore->GetParent(); searchView;
|
|
|
|
searchView = searchView->GetParent()) {
|
|
|
|
f = NS_STATIC_CAST(nsIFrame*, searchView->GetClientData());
|
|
|
|
if (f) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
NS_ASSERTION(f, "Can't find a frame anywhere!");
|
|
|
|
}
|
2005-03-24 04:15:07 +03:00
|
|
|
if (!f || !aFrame->GetContent() || !f->GetContent() ||
|
|
|
|
CompareTreePosition(aFrame->GetContent(), f->GetContent(), parentViewContent) > 0) {
|
|
|
|
// aFrame's content is after f's content (or we just don't know),
|
|
|
|
// so put our view before f's view
|
2003-10-15 04:49:03 +04:00
|
|
|
return insertBefore;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nsnull;
|
|
|
|
}
|
2004-08-06 19:55:17 +04:00
|
|
|
|
2004-11-03 05:52:16 +03:00
|
|
|
//static
|
|
|
|
nsIScrollableFrame*
|
|
|
|
nsLayoutUtils::GetScrollableFrameFor(nsIFrame *aScrolledFrame)
|
|
|
|
{
|
|
|
|
nsIFrame *frame = aScrolledFrame->GetParent();
|
|
|
|
if (!frame) {
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
nsIScrollableFrame *sf;
|
|
|
|
CallQueryInterface(frame, &sf);
|
|
|
|
return sf;
|
|
|
|
}
|
|
|
|
|
2004-09-21 08:41:08 +04:00
|
|
|
//static
|
2004-10-19 06:24:27 +04:00
|
|
|
nsIScrollableFrame*
|
|
|
|
nsLayoutUtils::GetScrollableFrameFor(nsIScrollableView *aScrollableView)
|
2004-09-21 08:41:08 +04:00
|
|
|
{
|
2005-04-04 01:00:41 +04:00
|
|
|
nsIFrame *frame = GetFrameFor(aScrollableView->View()->GetParent());
|
|
|
|
if (frame) {
|
2004-09-21 08:41:08 +04:00
|
|
|
nsIScrollableFrame *sf;
|
|
|
|
CallQueryInterface(frame, &sf);
|
2004-10-19 06:24:27 +04:00
|
|
|
return sf;
|
2004-09-21 08:41:08 +04:00
|
|
|
}
|
2004-10-19 06:24:27 +04:00
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
//static
|
|
|
|
nsPresContext::ScrollbarStyles
|
|
|
|
nsLayoutUtils::ScrollbarStylesOfView(nsIScrollableView *aScrollableView)
|
|
|
|
{
|
|
|
|
nsIScrollableFrame *sf = GetScrollableFrameFor(aScrollableView);
|
|
|
|
return sf ? sf->GetScrollbarStyles() :
|
|
|
|
nsPresContext::ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN,
|
|
|
|
NS_STYLE_OVERFLOW_HIDDEN);
|
2004-09-21 08:41:08 +04:00
|
|
|
}
|
|
|
|
|
2004-08-06 19:55:17 +04:00
|
|
|
// static
|
|
|
|
nsIScrollableView*
|
2004-09-21 08:41:08 +04:00
|
|
|
nsLayoutUtils::GetNearestScrollingView(nsIView* aView, Direction aDirection)
|
2004-08-06 19:55:17 +04:00
|
|
|
{
|
2004-11-19 06:49:20 +03:00
|
|
|
// If aDirection is eEither, find first view with a scrolllable frame.
|
|
|
|
// Otherwise, find the first view that has a scrollable frame whose
|
2004-10-19 06:24:27 +04:00
|
|
|
// ScrollbarStyles is not NS_STYLE_OVERFLOW_HIDDEN in aDirection
|
|
|
|
// and where there is something currently not visible
|
|
|
|
// that can be scrolled to in aDirection.
|
2004-08-06 19:55:17 +04:00
|
|
|
NS_ASSERTION(aView, "GetNearestScrollingView expects a non-null view");
|
|
|
|
nsIScrollableView* scrollableView = nsnull;
|
|
|
|
for (; aView; aView = aView->GetParent()) {
|
2004-11-24 03:19:21 +03:00
|
|
|
scrollableView = aView->ToScrollableView();
|
2004-08-06 19:55:17 +04:00
|
|
|
if (scrollableView) {
|
2004-09-21 08:41:08 +04:00
|
|
|
nsPresContext::ScrollbarStyles ss =
|
|
|
|
nsLayoutUtils::ScrollbarStylesOfView(scrollableView);
|
2004-10-19 06:24:27 +04:00
|
|
|
nsIScrollableFrame *scrollableFrame = GetScrollableFrameFor(scrollableView);
|
2005-06-27 02:22:40 +04:00
|
|
|
NS_ASSERTION(scrollableFrame, "Must have scrollable frame for view!");
|
2004-10-19 06:24:27 +04:00
|
|
|
nsMargin margin = scrollableFrame->GetActualScrollbarSizes();
|
|
|
|
// Get size of total scrollable area
|
|
|
|
nscoord totalWidth, totalHeight;
|
|
|
|
scrollableView->GetContainerSize(&totalWidth, &totalHeight);
|
|
|
|
// Get size of currently visible area
|
2005-04-04 01:00:41 +04:00
|
|
|
nsSize visibleSize = aView->GetBounds().Size();
|
2004-09-21 08:41:08 +04:00
|
|
|
// aDirection can be eHorizontal, eVertical, or eEither
|
2004-11-19 06:49:20 +03:00
|
|
|
// If scrolling in a specific direction, require visible scrollbars or
|
|
|
|
// something to scroll to in that direction.
|
2004-09-21 08:41:08 +04:00
|
|
|
if (aDirection != eHorizontal &&
|
2004-10-19 06:24:27 +04:00
|
|
|
ss.mVertical != NS_STYLE_OVERFLOW_HIDDEN &&
|
2004-11-19 06:49:20 +03:00
|
|
|
(aDirection == eEither || totalHeight > visibleSize.height || margin.right))
|
2004-09-21 08:41:08 +04:00
|
|
|
break;
|
|
|
|
if (aDirection != eVertical &&
|
2004-10-19 06:24:27 +04:00
|
|
|
ss.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN &&
|
2004-11-19 06:49:20 +03:00
|
|
|
(aDirection == eEither || totalWidth > visibleSize.width || margin.bottom))
|
2004-09-21 08:41:08 +04:00
|
|
|
break;
|
2004-08-06 19:55:17 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return scrollableView;
|
|
|
|
}
|
2004-11-25 17:51:00 +03:00
|
|
|
|
2005-06-02 13:00:48 +04:00
|
|
|
nsPoint
|
|
|
|
nsLayoutUtils::GetDOMEventCoordinatesRelativeTo(nsIDOMEvent* aDOMEvent, nsIFrame* aFrame)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIPrivateDOMEvent> privateEvent(do_QueryInterface(aDOMEvent));
|
|
|
|
NS_ASSERTION(privateEvent, "bad implementation");
|
|
|
|
if (!privateEvent)
|
|
|
|
return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
|
|
|
|
nsEvent* event;
|
|
|
|
nsresult rv = privateEvent->GetInternalNSEvent(&event);
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
|
2005-08-23 07:57:07 +04:00
|
|
|
return GetEventCoordinatesRelativeTo(event, aFrame);
|
|
|
|
}
|
|
|
|
|
|
|
|
nsPoint
|
|
|
|
nsLayoutUtils::GetEventCoordinatesRelativeTo(nsEvent* aEvent, nsIFrame* aFrame)
|
|
|
|
{
|
|
|
|
if (!aEvent || (aEvent->eventStructType != NS_MOUSE_EVENT &&
|
|
|
|
aEvent->eventStructType != NS_MOUSE_SCROLL_EVENT))
|
2005-06-02 13:00:48 +04:00
|
|
|
return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
|
2005-08-23 07:57:07 +04:00
|
|
|
|
|
|
|
nsGUIEvent* GUIEvent = NS_STATIC_CAST(nsGUIEvent*, aEvent);
|
2005-06-02 13:00:48 +04:00
|
|
|
if (!GUIEvent->widget)
|
|
|
|
return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
|
2005-08-23 07:57:07 +04:00
|
|
|
|
|
|
|
nsPoint frameToView;
|
|
|
|
nsIView* frameView = aFrame->GetClosestView(&frameToView);
|
|
|
|
|
2005-09-15 08:03:07 +04:00
|
|
|
nsPoint widgetToView = TranslateWidgetToView(aFrame->GetPresContext(),
|
2005-08-23 07:57:07 +04:00
|
|
|
GUIEvent->widget, GUIEvent->refPoint,
|
2005-09-15 08:03:07 +04:00
|
|
|
frameView);
|
|
|
|
|
|
|
|
if (widgetToView == nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE))
|
|
|
|
return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
|
|
|
|
|
|
|
|
return widgetToView - frameToView;
|
2005-08-23 07:57:07 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
nsPoint
|
|
|
|
nsLayoutUtils::GetEventCoordinatesForNearestView(nsEvent* aEvent,
|
|
|
|
nsIFrame* aFrame,
|
|
|
|
nsIView** aView)
|
|
|
|
{
|
|
|
|
if (!aEvent || (aEvent->eventStructType != NS_MOUSE_EVENT &&
|
|
|
|
aEvent->eventStructType != NS_MOUSE_SCROLL_EVENT))
|
|
|
|
return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
|
|
|
|
|
|
|
|
nsGUIEvent* GUIEvent = NS_STATIC_CAST(nsGUIEvent*, aEvent);
|
|
|
|
if (!GUIEvent->widget)
|
2005-06-02 13:00:48 +04:00
|
|
|
return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
|
|
|
|
|
|
|
|
nsPoint viewToFrame;
|
2005-08-23 07:57:07 +04:00
|
|
|
nsIView* frameView;
|
|
|
|
aFrame->GetOffsetFromView(viewToFrame, &frameView);
|
|
|
|
if (aView)
|
|
|
|
*aView = frameView;
|
2005-06-02 13:00:48 +04:00
|
|
|
|
2005-08-23 07:57:07 +04:00
|
|
|
return TranslateWidgetToView(aFrame->GetPresContext(), GUIEvent->widget,
|
|
|
|
GUIEvent->refPoint, frameView);
|
|
|
|
}
|
|
|
|
|
|
|
|
nsPoint
|
|
|
|
nsLayoutUtils::TranslateWidgetToView(nsPresContext* aPresContext,
|
|
|
|
nsIWidget* aWidget, nsIntPoint aPt,
|
|
|
|
nsIView* aView)
|
|
|
|
{
|
|
|
|
nsIView* baseView = nsIView::GetViewFor(aWidget);
|
2005-09-15 08:03:07 +04:00
|
|
|
if (!baseView)
|
|
|
|
return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
|
2005-09-18 22:03:06 +04:00
|
|
|
nsPoint viewToWidget;
|
|
|
|
nsIWidget* wid = baseView->GetNearestWidget(&viewToWidget);
|
2005-08-23 07:57:07 +04:00
|
|
|
NS_ASSERTION(aWidget == wid, "Clashing widgets");
|
|
|
|
float pixelsToTwips = aPresContext->PixelsToTwips();
|
|
|
|
nsPoint refPointTwips(NSIntPixelsToTwips(aPt.x, pixelsToTwips),
|
|
|
|
NSIntPixelsToTwips(aPt.y, pixelsToTwips));
|
2005-09-18 22:03:06 +04:00
|
|
|
return refPointTwips - viewToWidget - aView->GetOffsetTo(baseView);
|
2005-06-02 13:00:48 +04:00
|
|
|
}
|
|
|
|
|
2004-11-25 17:51:00 +03:00
|
|
|
// Combine aNewBreakType with aOrigBreakType, but limit the break types
|
|
|
|
// to NS_STYLE_CLEAR_LEFT, RIGHT, LEFT_AND_RIGHT.
|
|
|
|
PRUint8
|
|
|
|
nsLayoutUtils::CombineBreakType(PRUint8 aOrigBreakType,
|
|
|
|
PRUint8 aNewBreakType)
|
|
|
|
{
|
|
|
|
PRUint8 breakType = aOrigBreakType;
|
|
|
|
switch(breakType) {
|
|
|
|
case NS_STYLE_CLEAR_LEFT:
|
|
|
|
if ((NS_STYLE_CLEAR_RIGHT == aNewBreakType) ||
|
|
|
|
(NS_STYLE_CLEAR_LEFT_AND_RIGHT == aNewBreakType)) {
|
|
|
|
breakType = NS_STYLE_CLEAR_LEFT_AND_RIGHT;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case NS_STYLE_CLEAR_RIGHT:
|
|
|
|
if ((NS_STYLE_CLEAR_LEFT == aNewBreakType) ||
|
|
|
|
(NS_STYLE_CLEAR_LEFT_AND_RIGHT == aNewBreakType)) {
|
|
|
|
breakType = NS_STYLE_CLEAR_LEFT_AND_RIGHT;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case NS_STYLE_CLEAR_NONE:
|
|
|
|
if ((NS_STYLE_CLEAR_LEFT == aNewBreakType) ||
|
|
|
|
(NS_STYLE_CLEAR_RIGHT == aNewBreakType) ||
|
|
|
|
(NS_STYLE_CLEAR_LEFT_AND_RIGHT == aNewBreakType)) {
|
|
|
|
breakType = aNewBreakType;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return breakType;
|
|
|
|
}
|
2005-02-08 05:15:26 +03:00
|
|
|
|
|
|
|
PRBool
|
|
|
|
nsLayoutUtils::IsInitialContainingBlock(nsIFrame* aFrame)
|
|
|
|
{
|
|
|
|
return aFrame ==
|
|
|
|
aFrame->GetPresContext()->PresShell()->FrameConstructor()->GetInitialContainingBlock();
|
|
|
|
}
|
2005-12-06 03:32:52 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Uses a binary search for find where the cursor falls in the line of text
|
|
|
|
* It also keeps track of the part of the string that has already been measured
|
|
|
|
* so it doesn't have to keep measuring the same text over and over
|
|
|
|
*
|
|
|
|
* @param "aBaseWidth" contains the width in twips of the portion
|
|
|
|
* of the text that has already been measured, and aBaseInx contains
|
|
|
|
* the index of the text that has already been measured.
|
|
|
|
*
|
|
|
|
* @param aTextWidth returns the (in twips) the length of the text that falls
|
|
|
|
* before the cursor aIndex contains the index of the text where the cursor falls
|
|
|
|
*/
|
|
|
|
PRBool
|
|
|
|
nsLayoutUtils::BinarySearchForPosition(nsIRenderingContext* aRendContext,
|
|
|
|
const PRUnichar* aText,
|
|
|
|
PRInt32 aBaseWidth,
|
|
|
|
PRInt32 aBaseInx,
|
|
|
|
PRInt32 aStartInx,
|
|
|
|
PRInt32 aEndInx,
|
|
|
|
PRInt32 aCursorPos,
|
|
|
|
PRInt32& aIndex,
|
|
|
|
PRInt32& aTextWidth)
|
|
|
|
{
|
|
|
|
PRInt32 range = aEndInx - aStartInx;
|
|
|
|
if ((range == 1) || (range == 2 && IS_HIGH_SURROGATE(aText[aStartInx]))) {
|
|
|
|
aIndex = aStartInx + aBaseInx;
|
|
|
|
aRendContext->GetWidth(aText, aIndex, aTextWidth);
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRInt32 inx = aStartInx + (range / 2);
|
|
|
|
|
|
|
|
// Make sure we don't leave a dangling low surrogate
|
|
|
|
if (IS_HIGH_SURROGATE(aText[inx-1]))
|
|
|
|
inx++;
|
|
|
|
|
|
|
|
PRInt32 textWidth = 0;
|
|
|
|
aRendContext->GetWidth(aText, inx, textWidth);
|
|
|
|
|
|
|
|
PRInt32 fullWidth = aBaseWidth + textWidth;
|
|
|
|
if (fullWidth == aCursorPos) {
|
|
|
|
aTextWidth = textWidth;
|
|
|
|
aIndex = inx;
|
|
|
|
return PR_TRUE;
|
|
|
|
} else if (aCursorPos < fullWidth) {
|
|
|
|
aTextWidth = aBaseWidth;
|
|
|
|
if (BinarySearchForPosition(aRendContext, aText, aBaseWidth, aBaseInx, aStartInx, inx, aCursorPos, aIndex, aTextWidth)) {
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
aTextWidth = fullWidth;
|
|
|
|
if (BinarySearchForPosition(aRendContext, aText, aBaseWidth, aBaseInx, inx, aEndInx, aCursorPos, aIndex, aTextWidth)) {
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
2005-12-13 02:53:06 +03:00
|
|
|
void
|
|
|
|
nsLayoutUtils::ScrollIntoView(nsIFormControlFrame* aFormFrame)
|
|
|
|
{
|
|
|
|
NS_ASSERTION(aFormFrame, "Null frame passed into ScrollIntoView");
|
|
|
|
nsIFrame* frame = nsnull;
|
|
|
|
CallQueryInterface(aFormFrame, &frame);
|
|
|
|
NS_ASSERTION(frame, "Form frame did not implement nsIFrame.");
|
|
|
|
if (frame) {
|
|
|
|
nsIPresShell* presShell = frame->GetPresContext()->GetPresShell();
|
|
|
|
if (presShell) {
|
|
|
|
presShell->ScrollFrameIntoView(frame,
|
|
|
|
NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE,
|
|
|
|
NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|