Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / core / layout / LayoutFrameSet.cpp
blob5b06662267dfeb189a6a1db6713019703842aff1
1 /**
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 2000 Simon Hausmann <hausmann@kde.org>
4 * (C) 2000 Stefan Schimanski (1Stein@gmx.de)
5 * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
24 #include "config.h"
25 #include "core/layout/LayoutFrameSet.h"
27 #include "core/dom/Document.h"
28 #include "core/events/MouseEvent.h"
29 #include "core/frame/LocalFrame.h"
30 #include "core/html/HTMLDimension.h"
31 #include "core/html/HTMLFrameSetElement.h"
32 #include "core/input/EventHandler.h"
33 #include "core/layout/LayoutFrame.h"
34 #include "core/layout/LayoutView.h"
35 #include "core/paint/FrameSetPainter.h"
36 #include "platform/Cursor.h"
37 #include "platform/graphics/GraphicsContext.h"
39 namespace blink {
41 LayoutFrameSet::LayoutFrameSet(HTMLFrameSetElement* frameSet)
42 : LayoutBox(frameSet)
43 , m_isResizing(false)
44 , m_isChildResizing(false)
46 setInline(false);
49 LayoutFrameSet::~LayoutFrameSet()
53 LayoutFrameSet::GridAxis::GridAxis()
54 : m_splitBeingResized(noSplit)
58 HTMLFrameSetElement* LayoutFrameSet::frameSet() const
60 return toHTMLFrameSetElement(node());
63 void LayoutFrameSet::paint(const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
65 FrameSetPainter(*this).paint(paintInfo, paintOffset);
68 void LayoutFrameSet::computePreferredLogicalWidths()
70 m_minPreferredLogicalWidth = 0;
71 m_maxPreferredLogicalWidth = 0;
72 clearPreferredLogicalWidthsDirty();
75 void LayoutFrameSet::GridAxis::resize(int size)
77 m_sizes.resize(size);
78 m_deltas.resize(size);
79 m_deltas.fill(0);
81 // To track edges for resizability and borders, we need to be (size + 1). This is because a parent frameset
82 // may ask us for information about our left/top/right/bottom edges in order to make its own decisions about
83 // what to do. We are capable of tainting that parent frameset's borders, so we have to cache this info.
84 m_preventResize.resize(size + 1);
85 m_allowBorder.resize(size + 1);
88 void LayoutFrameSet::layOutAxis(GridAxis& axis, const Vector<HTMLDimension>& grid, int availableLen)
90 availableLen = max(availableLen, 0);
92 int* gridLayout = axis.m_sizes.data();
94 if (grid.isEmpty()) {
95 gridLayout[0] = availableLen;
96 return;
99 int gridLen = axis.m_sizes.size();
100 ASSERT(gridLen);
102 int totalRelative = 0;
103 int totalFixed = 0;
104 int totalPercent = 0;
105 int countRelative = 0;
106 int countFixed = 0;
107 int countPercent = 0;
109 // First we need to investigate how many columns of each type we have and
110 // how much space these columns are going to require.
111 for (int i = 0; i < gridLen; ++i) {
112 // Count the total length of all of the fixed columns/rows -> totalFixed
113 // Count the number of columns/rows which are fixed -> countFixed
114 if (grid[i].isAbsolute()) {
115 gridLayout[i] = max<int>(grid[i].value(), 0);
116 totalFixed += gridLayout[i];
117 countFixed++;
120 // Count the total percentage of all of the percentage columns/rows -> totalPercent
121 // Count the number of columns/rows which are percentages -> countPercent
122 if (grid[i].isPercentage()) {
123 gridLayout[i] = max<int>(grid[i].value() * availableLen / 100., 0);
124 totalPercent += gridLayout[i];
125 countPercent++;
128 // Count the total relative of all the relative columns/rows -> totalRelative
129 // Count the number of columns/rows which are relative -> countRelative
130 if (grid[i].isRelative()) {
131 totalRelative += max<int>(grid[i].value(), 1);
132 countRelative++;
136 int remainingLen = availableLen;
138 // Fixed columns/rows are our first priority. If there is not enough space to fit all fixed
139 // columns/rows we need to proportionally adjust their size.
140 if (totalFixed > remainingLen) {
141 int remainingFixed = remainingLen;
143 for (int i = 0; i < gridLen; ++i) {
144 if (grid[i].isAbsolute()) {
145 gridLayout[i] = (gridLayout[i] * remainingFixed) / totalFixed;
146 remainingLen -= gridLayout[i];
149 } else {
150 remainingLen -= totalFixed;
153 // Percentage columns/rows are our second priority. Divide the remaining space proportionally
154 // over all percentage columns/rows. IMPORTANT: the size of each column/row is not relative
155 // to 100%, but to the total percentage. For example, if there are three columns, each of 75%,
156 // and the available space is 300px, each column will become 100px in width.
157 if (totalPercent > remainingLen) {
158 int remainingPercent = remainingLen;
160 for (int i = 0; i < gridLen; ++i) {
161 if (grid[i].isPercentage()) {
162 gridLayout[i] = (gridLayout[i] * remainingPercent) / totalPercent;
163 remainingLen -= gridLayout[i];
166 } else {
167 remainingLen -= totalPercent;
170 // Relative columns/rows are our last priority. Divide the remaining space proportionally
171 // over all relative columns/rows. IMPORTANT: the relative value of 0* is treated as 1*.
172 if (countRelative) {
173 int lastRelative = 0;
174 int remainingRelative = remainingLen;
176 for (int i = 0; i < gridLen; ++i) {
177 if (grid[i].isRelative()) {
178 gridLayout[i] = (max(grid[i].value(), 1.) * remainingRelative) / totalRelative;
179 remainingLen -= gridLayout[i];
180 lastRelative = i;
184 // If we could not evenly distribute the available space of all of the relative
185 // columns/rows, the remainder will be added to the last column/row.
186 // For example: if we have a space of 100px and three columns (*,*,*), the remainder will
187 // be 1px and will be added to the last column: 33px, 33px, 34px.
188 if (remainingLen) {
189 gridLayout[lastRelative] += remainingLen;
190 remainingLen = 0;
194 // If we still have some left over space we need to divide it over the already existing
195 // columns/rows
196 if (remainingLen) {
197 // Our first priority is to spread if over the percentage columns. The remaining
198 // space is spread evenly, for example: if we have a space of 100px, the columns
199 // definition of 25%,25% used to result in two columns of 25px. After this the
200 // columns will each be 50px in width.
201 if (countPercent && totalPercent) {
202 int remainingPercent = remainingLen;
203 int changePercent = 0;
205 for (int i = 0; i < gridLen; ++i) {
206 if (grid[i].isPercentage()) {
207 changePercent = (remainingPercent * gridLayout[i]) / totalPercent;
208 gridLayout[i] += changePercent;
209 remainingLen -= changePercent;
212 } else if (totalFixed) {
213 // Our last priority is to spread the remaining space over the fixed columns.
214 // For example if we have 100px of space and two column of each 40px, both
215 // columns will become exactly 50px.
216 int remainingFixed = remainingLen;
217 int changeFixed = 0;
219 for (int i = 0; i < gridLen; ++i) {
220 if (grid[i].isAbsolute()) {
221 changeFixed = (remainingFixed * gridLayout[i]) / totalFixed;
222 gridLayout[i] += changeFixed;
223 remainingLen -= changeFixed;
229 // If we still have some left over space we probably ended up with a remainder of
230 // a division. We cannot spread it evenly anymore. If we have any percentage
231 // columns/rows simply spread the remainder equally over all available percentage columns,
232 // regardless of their size.
233 if (remainingLen && countPercent) {
234 int remainingPercent = remainingLen;
235 int changePercent = 0;
237 for (int i = 0; i < gridLen; ++i) {
238 if (grid[i].isPercentage()) {
239 changePercent = remainingPercent / countPercent;
240 gridLayout[i] += changePercent;
241 remainingLen -= changePercent;
244 } else if (remainingLen && countFixed) {
245 // If we don't have any percentage columns/rows we only have
246 // fixed columns. Spread the remainder equally over all fixed
247 // columns/rows.
248 int remainingFixed = remainingLen;
249 int changeFixed = 0;
251 for (int i = 0; i < gridLen; ++i) {
252 if (grid[i].isAbsolute()) {
253 changeFixed = remainingFixed / countFixed;
254 gridLayout[i] += changeFixed;
255 remainingLen -= changeFixed;
260 // Still some left over. Add it to the last column, because it is impossible
261 // spread it evenly or equally.
262 if (remainingLen)
263 gridLayout[gridLen - 1] += remainingLen;
265 // now we have the final layout, distribute the delta over it
266 bool worked = true;
267 int* gridDelta = axis.m_deltas.data();
268 for (int i = 0; i < gridLen; ++i) {
269 if (gridLayout[i] && gridLayout[i] + gridDelta[i] <= 0)
270 worked = false;
271 gridLayout[i] += gridDelta[i];
273 // if the deltas broke something, undo them
274 if (!worked) {
275 for (int i = 0; i < gridLen; ++i)
276 gridLayout[i] -= gridDelta[i];
277 axis.m_deltas.fill(0);
281 void LayoutFrameSet::notifyFrameEdgeInfoChanged()
283 if (needsLayout())
284 return;
285 // FIXME: We should only recompute the edge info with respect to the frame that changed
286 // and its adjacent frame(s) instead of recomputing the edge info for the entire frameset.
287 computeEdgeInfo();
290 void LayoutFrameSet::fillFromEdgeInfo(const FrameEdgeInfo& edgeInfo, int r, int c)
292 if (edgeInfo.allowBorder(LeftFrameEdge))
293 m_cols.m_allowBorder[c] = true;
294 if (edgeInfo.allowBorder(RightFrameEdge))
295 m_cols.m_allowBorder[c + 1] = true;
296 if (edgeInfo.preventResize(LeftFrameEdge))
297 m_cols.m_preventResize[c] = true;
298 if (edgeInfo.preventResize(RightFrameEdge))
299 m_cols.m_preventResize[c + 1] = true;
301 if (edgeInfo.allowBorder(TopFrameEdge))
302 m_rows.m_allowBorder[r] = true;
303 if (edgeInfo.allowBorder(BottomFrameEdge))
304 m_rows.m_allowBorder[r + 1] = true;
305 if (edgeInfo.preventResize(TopFrameEdge))
306 m_rows.m_preventResize[r] = true;
307 if (edgeInfo.preventResize(BottomFrameEdge))
308 m_rows.m_preventResize[r + 1] = true;
311 void LayoutFrameSet::computeEdgeInfo()
313 m_rows.m_preventResize.fill(frameSet()->noResize());
314 m_rows.m_allowBorder.fill(false);
315 m_cols.m_preventResize.fill(frameSet()->noResize());
316 m_cols.m_allowBorder.fill(false);
318 LayoutObject* child = firstChild();
319 if (!child)
320 return;
322 size_t rows = m_rows.m_sizes.size();
323 size_t cols = m_cols.m_sizes.size();
324 for (size_t r = 0; r < rows; ++r) {
325 for (size_t c = 0; c < cols; ++c) {
326 FrameEdgeInfo edgeInfo;
327 if (child->isFrameSet())
328 edgeInfo = toLayoutFrameSet(child)->edgeInfo();
329 else
330 edgeInfo = toLayoutFrame(child)->edgeInfo();
331 fillFromEdgeInfo(edgeInfo, r, c);
332 child = child->nextSibling();
333 if (!child)
334 return;
339 FrameEdgeInfo LayoutFrameSet::edgeInfo() const
341 FrameEdgeInfo result(frameSet()->noResize(), true);
343 int rows = frameSet()->totalRows();
344 int cols = frameSet()->totalCols();
345 if (rows && cols) {
346 result.setPreventResize(LeftFrameEdge, m_cols.m_preventResize[0]);
347 result.setAllowBorder(LeftFrameEdge, m_cols.m_allowBorder[0]);
348 result.setPreventResize(RightFrameEdge, m_cols.m_preventResize[cols]);
349 result.setAllowBorder(RightFrameEdge, m_cols.m_allowBorder[cols]);
350 result.setPreventResize(TopFrameEdge, m_rows.m_preventResize[0]);
351 result.setAllowBorder(TopFrameEdge, m_rows.m_allowBorder[0]);
352 result.setPreventResize(BottomFrameEdge, m_rows.m_preventResize[rows]);
353 result.setAllowBorder(BottomFrameEdge, m_rows.m_allowBorder[rows]);
356 return result;
359 void LayoutFrameSet::layout()
361 ASSERT(needsLayout());
363 if (!parent()->isFrameSet() && !document().printing()) {
364 setWidth(view()->viewWidth());
365 setHeight(view()->viewHeight());
368 unsigned cols = frameSet()->totalCols();
369 unsigned rows = frameSet()->totalRows();
371 if (m_rows.m_sizes.size() != rows || m_cols.m_sizes.size() != cols) {
372 m_rows.resize(rows);
373 m_cols.resize(cols);
376 LayoutUnit borderThickness = frameSet()->border();
377 layOutAxis(m_rows, frameSet()->rowLengths(), size().height() - (rows - 1) * borderThickness);
378 layOutAxis(m_cols, frameSet()->colLengths(), size().width() - (cols - 1) * borderThickness);
380 positionFrames();
382 LayoutBox::layout();
384 computeEdgeInfo();
386 updateLayerTransformAfterLayout();
388 clearNeedsLayout();
391 static void clearNeedsLayoutOnHiddenFrames(LayoutBox* frame)
393 for (; frame; frame = frame->nextSiblingBox()) {
394 frame->setWidth(0);
395 frame->setHeight(0);
396 frame->clearNeedsLayout();
397 clearNeedsLayoutOnHiddenFrames(frame->firstChildBox());
401 void LayoutFrameSet::positionFrames()
403 LayoutBox* child = firstChildBox();
404 if (!child)
405 return;
407 int rows = frameSet()->totalRows();
408 int cols = frameSet()->totalCols();
410 int borderThickness = frameSet()->border();
411 LayoutSize size;
412 LayoutPoint position;
413 for (int r = 0; r < rows; r++) {
414 position.setX(0);
415 size.setHeight(m_rows.m_sizes[r]);
416 for (int c = 0; c < cols; c++) {
417 child->setLocation(position);
418 size.setWidth(m_cols.m_sizes[c]);
420 // has to be resized and itself resize its contents
421 if (size != child->size()) {
422 child->setSize(size);
423 child->setNeedsLayoutAndFullPaintInvalidation(LayoutInvalidationReason::SizeChanged);
424 child->layout();
427 position.setX(position.x() + size.width() + borderThickness);
429 child = child->nextSiblingBox();
430 if (!child)
431 return;
433 position.setY(position.y() + size.height() + borderThickness);
436 // All the remaining frames are hidden to avoid ugly spurious unflowed frames.
437 clearNeedsLayoutOnHiddenFrames(child);
440 void LayoutFrameSet::startResizing(GridAxis& axis, int position)
442 int split = hitTestSplit(axis, position);
443 if (split == noSplit || axis.m_preventResize[split]) {
444 axis.m_splitBeingResized = noSplit;
445 return;
447 axis.m_splitBeingResized = split;
448 axis.m_splitResizeOffset = position - splitPosition(axis, split);
451 void LayoutFrameSet::continueResizing(GridAxis& axis, int position)
453 if (needsLayout())
454 return;
455 if (axis.m_splitBeingResized == noSplit)
456 return;
457 int currentSplitPosition = splitPosition(axis, axis.m_splitBeingResized);
458 int delta = (position - currentSplitPosition) - axis.m_splitResizeOffset;
459 if (!delta)
460 return;
461 axis.m_deltas[axis.m_splitBeingResized - 1] += delta;
462 axis.m_deltas[axis.m_splitBeingResized] -= delta;
463 setNeedsLayoutAndFullPaintInvalidation(LayoutInvalidationReason::SizeChanged);
466 bool LayoutFrameSet::userResize(MouseEvent* evt)
468 if (!m_isResizing) {
469 if (needsLayout())
470 return false;
471 if (evt->type() == EventTypeNames::mousedown && evt->button() == LeftButton) {
472 FloatPoint localPos = absoluteToLocal(FloatPoint(evt->absoluteLocation()), UseTransforms);
473 startResizing(m_cols, localPos.x());
474 startResizing(m_rows, localPos.y());
475 if (m_cols.m_splitBeingResized != noSplit || m_rows.m_splitBeingResized != noSplit) {
476 setIsResizing(true);
477 return true;
480 } else {
481 if (evt->type() == EventTypeNames::mousemove || (evt->type() == EventTypeNames::mouseup && evt->button() == LeftButton)) {
482 FloatPoint localPos = absoluteToLocal(FloatPoint(evt->absoluteLocation()), UseTransforms);
483 continueResizing(m_cols, localPos.x());
484 continueResizing(m_rows, localPos.y());
485 if (evt->type() == EventTypeNames::mouseup && evt->button() == LeftButton) {
486 setIsResizing(false);
487 return true;
492 return false;
495 void LayoutFrameSet::setIsResizing(bool isResizing)
497 m_isResizing = isResizing;
498 for (LayoutObject* ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
499 if (ancestor->isFrameSet())
500 toLayoutFrameSet(ancestor)->m_isChildResizing = isResizing;
502 if (LocalFrame* frame = this->frame())
503 frame->eventHandler().setResizingFrameSet(isResizing ? frameSet() : 0);
506 bool LayoutFrameSet::canResizeRow(const IntPoint& p) const
508 int r = hitTestSplit(m_rows, p.y());
509 return r != noSplit && !m_rows.m_preventResize[r];
512 bool LayoutFrameSet::canResizeColumn(const IntPoint& p) const
514 int c = hitTestSplit(m_cols, p.x());
515 return c != noSplit && !m_cols.m_preventResize[c];
518 int LayoutFrameSet::splitPosition(const GridAxis& axis, int split) const
520 if (needsLayout())
521 return 0;
523 int borderThickness = frameSet()->border();
525 int size = axis.m_sizes.size();
526 if (!size)
527 return 0;
529 int position = 0;
530 for (int i = 0; i < split && i < size; ++i)
531 position += axis.m_sizes[i] + borderThickness;
532 return position - borderThickness;
535 int LayoutFrameSet::hitTestSplit(const GridAxis& axis, int position) const
537 if (needsLayout())
538 return noSplit;
540 int borderThickness = frameSet()->border();
541 if (borderThickness <= 0)
542 return noSplit;
544 size_t size = axis.m_sizes.size();
545 if (!size)
546 return noSplit;
548 int splitPosition = axis.m_sizes[0];
549 for (size_t i = 1; i < size; ++i) {
550 if (position >= splitPosition && position < splitPosition + borderThickness)
551 return i;
552 splitPosition += borderThickness + axis.m_sizes[i];
554 return noSplit;
557 bool LayoutFrameSet::isChildAllowed(LayoutObject* child, const ComputedStyle&) const
559 return child->isFrame() || child->isFrameSet();
562 CursorDirective LayoutFrameSet::getCursor(const LayoutPoint& point, Cursor& cursor) const
564 IntPoint roundedPoint = roundedIntPoint(point);
565 if (canResizeRow(roundedPoint)) {
566 cursor = rowResizeCursor();
567 return SetCursor;
569 if (canResizeColumn(roundedPoint)) {
570 cursor = columnResizeCursor();
571 return SetCursor;
573 return LayoutBox::getCursor(point, cursor);
576 } // namespace blink