Commit 5083f636 authored by jchaffraix@webkit.org's avatar jchaffraix@webkit.org

Revert r127457 and following fixes due to several hit-testing regressions

https://bugs.webkit.org/show_bug.cgi?id=96830

Reviewed by Antonio Gomes.

.:

This change reverts r127457, r127863 and r128505.

* Source/autotools/symbols.filter:

Source/WebCore:

This change reverts r127457, r127863 and r128505.

* WebCore.exp.in:
* WebCore.order:
* dom/Document.cpp:
(WebCore::Document::nodesFromRect):
* dom/Document.h:
(Document):
* page/EventHandler.cpp:
(WebCore::EventHandler::hitTestResultAtPoint):
* rendering/HitTestRequest.h:
* rendering/HitTestResult.cpp:
(WebCore::HitTestLocation::HitTestLocation):
* rendering/HitTestResult.h:
(HitTestLocation):
* rendering/RenderFrameBase.cpp:
* rendering/RenderFrameBase.h:
(RenderFrameBase):
* rendering/RenderLayer.cpp:
(WebCore::RenderLayer::hitTest):
* testing/Internals.cpp:
(WebCore::Internals::nodesFromRect):
* testing/Internals.h:
(Internals):
* testing/Internals.idl:

Source/WebKit/chromium:

This change reverts r127457, r127863 and r128505.

* src/ContextMenuClientImpl.cpp:
(WebKit::ContextMenuClientImpl::getCustomMenuFromDefaultItems):

Source/WebKit2:

This change reverts r127457, r127863 and r128505.

* win/WebKit2.def:
* win/WebKit2CFLite.def:

LayoutTests:

This change reverts r127457, r127863 and r128505. As the tests relies on the extended API
after r127457, we cannot keep them.

* fast/dom/nodesFromRect/nodesFromRect-child-frame-content-expected.txt: Removed.
* fast/dom/nodesFromRect/nodesFromRect-child-frame-content.html: Removed.
* fast/dom/nodesFromRect/resources/child-frame.html: Removed.
* fast/dom/nodesFromRect/resources/nodesFromRect.js:
(check):
(checkShadowContent):
* touchadjustment/iframe-boundary-expected.txt: Removed.
* touchadjustment/iframe-boundary.html: Removed.
* touchadjustment/resources/inner-content-page.html: Removed.
* touchadjustment/resources/inner-navigation-frame.html: Removed.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@128677 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent fe91d738
2012-09-14 Julien Chaffraix <jchaffraix@webkit.org>
Revert r127457 and following fixes due to several hit-testing regressions
https://bugs.webkit.org/show_bug.cgi?id=96830
Reviewed by Antonio Gomes.
This change reverts r127457, r127863 and r128505.
* Source/autotools/symbols.filter:
2012-09-14 Adam Barth <abarth@webkit.org>
Remove webkitPostMessage
......
2012-09-14 Julien Chaffraix <jchaffraix@webkit.org>
Revert r127457 and following fixes due to several hit-testing regressions
https://bugs.webkit.org/show_bug.cgi?id=96830
Reviewed by Antonio Gomes.
This change reverts r127457, r127863 and r128505. As the tests relies on the extended API
after r127457, we cannot keep them.
* fast/dom/nodesFromRect/nodesFromRect-child-frame-content-expected.txt: Removed.
* fast/dom/nodesFromRect/nodesFromRect-child-frame-content.html: Removed.
* fast/dom/nodesFromRect/resources/child-frame.html: Removed.
* fast/dom/nodesFromRect/resources/nodesFromRect.js:
(check):
(checkShadowContent):
* touchadjustment/iframe-boundary-expected.txt: Removed.
* touchadjustment/iframe-boundary.html: Removed.
* touchadjustment/resources/inner-content-page.html: Removed.
* touchadjustment/resources/inner-navigation-frame.html: Removed.
2012-09-14 Bo Liu <boliu@chromium.org>
Skip image load tests that requires queueReload in WebKit2
Document::nodesFromRect : Allow child-frame content - bug 95204
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS All correct nodes found for rect
PASS All correct nodes found for rect
PASS All correct nodes found for rect
PASS All correct nodes found for rect
PASS All correct nodes found for rect
PASS All correct nodes found for rect
PASS All correct nodes found for rect
PASS All correct nodes found for rect
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE html>
<html>
<head>
<title>Document::nodesFromRect : Allow child-frame content - bug 95204</title>
<script src="../../js/resources/js-test-pre.js"></script>
<script src="resources/nodesFromRect.js"></script>
<style>
body { margin: 0px; }
#sandbox {
width: 400px;
height: 200px;
}
#sandbox #layer {
float: right;
}
#sandbox iframe {
display: block;
box-sizing: border-box;
width: 200px;
height: 200px;
border: none;
}
.rotate180 { -webkit-transform: rotate(180deg); }
.rotate90 { -webkit-transform: rotate(90deg); }
#sandbox .box {
box-sizing: border-box;
height: 100px;
width: 200px;
border: 1px solid black;
}
</style>
</head>
<body id="body">
<div id=sandbox>
<div id=layer>
<iframe id="iframe1" src="resources/child-frame.html"></iframe>
</div>
<div id=div1 class=box></div>
<div id=div2 class=box></div>
</div>
<p id='description'></p>
<div id="console"></div>
<script type="application/javascript">
function runTest()
{
description(document.title);
// Set up shortcut access to elements
var e = {};
['sandbox', 'layer'].forEach(function(a) {
e[a] = document.getElementById(a);
});
checkRect(25, 25, 100, 100, "DIV#div2, DIV#div1, DIV#sandbox");
checkRect(220, 20, 70, 70, "DIV#left");
checkRect(250, 20, 100, 70, "DIV#right, DIV#left, HTML");
checkRect(150, 50, 100, 100, "DIV#left, HTML, #document, IFRAME#iframe1, DIV#layer, DIV#div2, DIV#div1, DIV#sandbox");
e.layer.setAttribute('class', 'rotate180');
checkRect(220, 20, 70, 70, "DIV#right");
checkRect(150, 50, 100, 100, "DIV#right, HTML, #document, IFRAME#iframe1, DIV#layer, DIV#div2, DIV#div1, DIV#sandbox");
e.layer.setAttribute('class', 'rotate90');
checkRect(250, 20, 100, 70, "DIV#left");
checkRect(150, 20, 200, 60, "DIV#left, HTML, #document, IFRAME#iframe1, DIV#layer, DIV#div1, DIV#sandbox");
e.sandbox.display = 'none';
finishJSTest();
}
jsTestIsAsync = true;
window.onload = runTest;
</script>
<script src="../../js/resources/js-test-post.js"></script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
body { margin: 0px; }
div {
box-sizing: border-box;
border: 1px solid black;
}
#left {
float:left;
width: 90px;
height: 190px;
}
#right {
float:right;
width: 90px;
height: 190px;
}
</style>
</head>
<body>
<div id=left></div>
<div id=right></div>
</body>
</html>
/*
* Contributors:
* * Antonio Gomes <tonikitoo@webkit.org>
* * Allan Sandfeld Jensen <allan.jensen@nokia.com>
**/
**/
function check(x, y, topPadding, rightPadding, bottomPadding, leftPadding, list, doc)
{
......@@ -12,7 +11,7 @@ function check(x, y, topPadding, rightPadding, bottomPadding, leftPadding, list,
if (!doc)
doc = document;
var nodes = internals.nodesFromRect(doc, x, y, topPadding, rightPadding, bottomPadding, leftPadding, true /* ignoreClipping */, false /* allow shadow content */, false /* allow child-frame content */);
var nodes = internals.nodesFromRect(doc, x, y, topPadding, rightPadding, bottomPadding, leftPadding, true /* ignoreClipping */, false /* allow shadow content */);
if (!nodes)
return;
......@@ -46,7 +45,7 @@ function checkShadowContent(x, y, topPadding, rightPadding, bottomPadding, leftP
if (!doc)
doc = document;
var nodes = internals.nodesFromRect(doc, x, y, topPadding, rightPadding, bottomPadding, leftPadding, true /* ignoreClipping */, true /* allowShadowContent */, false /* allow child-frame content */);
var nodes = internals.nodesFromRect(doc, x, y, topPadding, rightPadding, bottomPadding, leftPadding, true /* ignoreClipping */, true /* allowShadowContent */);
if (!nodes)
return;
......@@ -68,61 +67,6 @@ function checkShadowContent(x, y, topPadding, rightPadding, bottomPadding, leftP
testPassed("All correct nodes found for rect");
}
function checkRect(left, top, width, height, expectedNodeString, doc)
{
if (!window.internals)
return;
if (height <=0 || width <= 0)
return;
if (!doc)
doc = document;
var topPadding = height / 2;
var leftPadding = width / 2;
// FIXME: When nodesFromRect is changed to not add 1 to width and height, remove the correction here.
var bottomPadding = (height - 1) - topPadding;
var rightPadding = (width - 1) - leftPadding;
var nodeString = nodesFromRectAsString(doc, left + leftPadding, top + topPadding, topPadding, rightPadding, bottomPadding, leftPadding);
if (nodeString == expectedNodeString) {
testPassed("All correct nodes found for rect");
} else {
testFailed("NodesFromRect should be [" + expectedNodeString + "] was [" + nodeString + "]");
}
}
function nodesFromRectAsString(doc, x, y, topPadding, rightPadding, bottomPadding, leftPadding)
{
var nodeString = "";
var nodes = internals.nodesFromRect(doc, x, y, topPadding, rightPadding, bottomPadding, leftPadding, true /* ignoreClipping */, true /* allow shadow content */, true /* allow child-frame content */);
if (!nodes)
return nodeString;
for (var i = 0; i < nodes.length; i++) {
if (nodes[i].nodeType == 1) {
nodeString += nodes[i].nodeName;
if (nodes[i].id)
nodeString += '#' + nodes[i].id;
else if (nodes[i].class) {
nodeString += '.' + nodes[i].class;
}
} else if (nodes[i].nodeType == 3) {
nodeString += "'" + nodes[i].data + "'";
} else if (nodes[i].nodeType == 9) {
nodeString += "#document";
} else {
continue;
}
if (i + 1 < nodes.length) {
nodeString += ", ";
}
}
return nodeString;
}
function getCenterFor(element)
{
var rect = element.getBoundingClientRect();
......
Test touch-adjustment at the boundary of an
Test direct touches.
PASS adjustedNode.id is "checkbox-1"
PASS adjustedNode.id is "button-1"
PASS adjustedNode.id is "checkbox-2"
PASS adjustedNode.id is "button-2"
PASS adjustedNode.id is "checkbox-3"
PASS adjustedNode.id is "button-3"
PASS adjustedNode.id is "checkbox-4"
PASS adjustedNode.id is "button-4"
Test indirect touches.
PASS adjustedNode.id is "checkbox-1"
PASS adjustedNode.id is "button-1"
PASS adjustedNode.id is "checkbox-2"
PASS adjustedNode.id is "button-2"
PASS adjustedNode.id is "checkbox-3"
PASS adjustedNode.id is "button-3"
PASS adjustedNode.id is "checkbox-4"
PASS adjustedNode.id is "button-4"
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE html>
<html>
<head>
<title>Touch Adjustment : Test touch adjustment at an iframe boundary - bug 94936</title>
<script src="../fast/js/resources/js-test-pre.js"></script>
<script src="resources/touchadjustment.js"></script>
<style>
#navbar {
background: #eee;
height: 100%;
left: 0;
padding: 0;
position: absolute;
margin: 0;
top: 0;
width: 100px;
}
#content-page {
height: 100%;
left: 100px;
position: absolute;
right: 0;
top: 0;
}
iframe {
border: none;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
</style>
</head>
<body id="main" onload="runTests()">
<div id="navbar" style="-webkit-transform: translateX(0px);">
<iframe id="nav-frame" src="resources/inner-navigation-frame.html"></iframe>
</div>
<div id="content-page">
<iframe id="content-frame" src="resources/inner-content-page.html"></iframe>
</div>
<p id='description'></p>
<div id='console'></div>
<script>
var itemHeight = 32;
var borderOffset = 100;
var checkboxOffset = 15;
var linkOffset = -15;
var checkboxOffsetIndirect = 5;
var linkOffsetIndirect = -5;
var touchRadius = 30;
function testRoundTouch(x, y, radius)
{
var x = x - radius;
var y = y - radius;
var width = radius * 2;
var height = radius * 2;
var adjustedNode = internals.touchNodeAdjustedToBestClickableNode(x, y, width, height, document);
if (adjustedNode.nodeType == 3) // TEXT node
adjustedNode = adjustedNode.parentNode;
return adjustedNode;
}
function testDirectTouches()
{
debug('Test direct touches.');
for (var i = 0; i < 4; i++) {
adjustedNode = testRoundTouch(borderOffset + checkboxOffset, (i + 0.5) * itemHeight, touchRadius);
shouldBeEqualToString('adjustedNode.id', 'checkbox-' + (i + 1));
adjustedNode = testRoundTouch(borderOffset + linkOffset, (i + 0.5) * itemHeight, touchRadius);
shouldBeEqualToString('adjustedNode.id', 'button-' + (i + 1));
}
}
function testIndirectTouches() {
debug('Test indirect touches.');
for (var i = 0; i < 4; i++) {
adjustedNode = testRoundTouch(borderOffset + checkboxOffsetIndirect, (i + 0.5) * itemHeight, touchRadius);
shouldBeEqualToString('adjustedNode.id', 'checkbox-' + (i + 1));
adjustedNode = testRoundTouch(borderOffset + linkOffsetIndirect, (i + 0.5) * itemHeight, touchRadius);
shouldBeEqualToString('adjustedNode.id', 'button-' + (i + 1));
}
}
function runTests()
{
if (window.testRunner && window.internals && internals.touchNodeAdjustedToBestClickableNode) {
description('Test touch-adjustment at the boundary of an <iframe>. Ensure that touch adjustment propagates into children of an <iframe>. Test expected to contain failures until bug 95204 is fixed.');
testDirectTouches();
testIndirectTouches();
finishJSTest();
}
}
jsTestIsAsync = true;
</script>
<script src="../fast/js/resources/js-test-post.js"></script>
</body>
</html>
<!DOCTYPE="html">
<html>
<head>
<style>
body {
background: white;
padding: 0;
margin: 0;
margin-left: 10px;
}
label {
display: block;
line-height: 22px;
margin: 5px;
}
</style>
</head>
<body>
<label><input id="checkbox-1" type="checkbox">Checkbox 1</label>
<label><input id="checkbox-2" type="checkbox">Checkbox 2</label>
<label><input id="checkbox-3" type="checkbox">Checkbox 3</label>
<label><input id="checkbox-4" type="checkbox">Checkbox 4</label>
</body>
</html>
<!DOCTYPE="html">
<html>
<head>
<style>
body {
padding: 0;
margin: 0;
}
div.button {
display: block;
text-align: center;
line-height: 18px;
margin: 5px;
width: 85px;
padding: 4px;
border: 1px solid black;
border-radius: 5px;
}
</style>
</head>
<body>
<script>
function onClick() {
}
</script>
<div class="button" id="button-1" onclick=onClick>Button 1</div>
<div class="button" id="button-2" onclick=onClick>Button 2</div>
<div class="button" id="button-3" onclick=onClick>Button 3</div>
<div class="button" id="button-4" onclick=onClick>Button 4</div>
</body>
</html>
2012-09-14 Julien Chaffraix <jchaffraix@webkit.org>
Revert r127457 and following fixes due to several hit-testing regressions
https://bugs.webkit.org/show_bug.cgi?id=96830
Reviewed by Antonio Gomes.
This change reverts r127457, r127863 and r128505.
* WebCore.exp.in:
* WebCore.order:
* dom/Document.cpp:
(WebCore::Document::nodesFromRect):
* dom/Document.h:
(Document):
* page/EventHandler.cpp:
(WebCore::EventHandler::hitTestResultAtPoint):
* rendering/HitTestRequest.h:
* rendering/HitTestResult.cpp:
(WebCore::HitTestLocation::HitTestLocation):
* rendering/HitTestResult.h:
(HitTestLocation):
* rendering/RenderFrameBase.cpp:
* rendering/RenderFrameBase.h:
(RenderFrameBase):
* rendering/RenderLayer.cpp:
(WebCore::RenderLayer::hitTest):
* testing/Internals.cpp:
(WebCore::Internals::nodesFromRect):
* testing/Internals.h:
(Internals):
* testing/Internals.idl:
2012-09-14 Joshua Bell <jsbell@chromium.org>
IndexedDB: Calling close() during upgradeneeded handler should fire error at open request
......@@ -1360,7 +1360,7 @@ __ZNK7WebCore7RunLoop9TimerBase8isActiveEv
__ZNK7WebCore8Document10renderViewEv
__ZNK7WebCore8Document11completeURLERKN3WTF6StringE
__ZNK7WebCore8Document13axObjectCacheEv
__ZNK7WebCore8Document13nodesFromRectEiijjjjbbb
__ZNK7WebCore8Document13nodesFromRectEiijjjjbb
__ZNK7WebCore8Document14getElementByIdERKN3WTF12AtomicStringE
__ZNK7WebCore8Document31displayStringModifiedByEncodingERKN3WTF6StringE
__ZNK7WebCore8Document4bodyEv
......
......@@ -22785,7 +22785,7 @@ __ZNK7WebCore12NodeIterator20updateForNodeRemovalEPNS_4NodeERNS0_11NodePointerE
__ZN7WebCore27jsNodeIteratorReferenceNodeEPN3JSC9ExecStateENS0_7JSValueERKNS0_10IdentifierE
__ZNK7WebCore4Node20traversePreviousNodeEPKS0_
__ZN7WebCore37jsNodeIteratorPrototypeFunctionDetachEPN3JSC9ExecStateE
__ZNK7WebCore8Document13nodesFromRectEiijjjjbbb
__ZNK7WebCore8Document13nodesFromRectEiijjjjb
__ZNK7WebCore8Document17handleZeroPaddingERKNS_14HitTestRequestERNS_13HitTestResultE
__ZN3WTF21ListHashSetTranslatorINS_6RefPtrIN7WebCore4NodeEEELm256ENS_7PtrHashIS4_EEE9translateERPNS_15ListHashSetNodeIS4_Lm256EEERKS4_PNS_24ListHashSetNodeAllocatorIS4_Lm256EEE
__ZN7WebCore21StaticHashSetNodeListC1ERN3WTF11ListHashSetINS1_6RefPtrINS_4NodeEEELm256ENS1_7PtrHashIS5_EEEE
......@@ -1392,7 +1392,7 @@ String Document::suggestedMIMEType() const
// * making it receive a rect as parameter, i.e. nodesFromRect(x, y, w, h);
// * making it receive the expading size of each direction separately,
// i.e. nodesFromRect(x, y, topSize, rightSize, bottomSize, leftSize);
PassRefPtr<NodeList> Document::nodesFromRect(int centerX, int centerY, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding, bool ignoreClipping, bool allowShadowContent, bool allowChildFrameContent) const
PassRefPtr<NodeList> Document::nodesFromRect(int centerX, int centerY, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding, bool ignoreClipping, bool allowShadowContent) const
{
// FIXME: Share code between this, elementFromPoint and caretRangeFromPoint.
if (!renderer())
......@@ -1416,8 +1416,6 @@ PassRefPtr<NodeList> Document::nodesFromRect(int centerX, int centerY, unsigned
return 0;
if (allowShadowContent)
type |= HitTestRequest::AllowShadowContent;
if (allowChildFrameContent)
type |= HitTestRequest::AllowChildFrameContent;
HitTestRequest request(type);
......
......@@ -381,9 +381,8 @@ public:
* @param ignoreClipping whether or not to ignore the root scroll frame when retrieving the element.
* If false, this method returns null for coordinates outside of the viewport.
*/
PassRefPtr<NodeList> nodesFromRect(int centerX, int centerY,
unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding,
bool ignoreClipping, bool allowShadowContent, bool allowChildFrameContent = false) const;
PassRefPtr<NodeList> nodesFromRect(int centerX, int centerY, unsigned topPadding, unsigned rightPadding,
unsigned bottomPadding, unsigned leftPadding, bool ignoreClipping, bool allowShadowContent) const;
Element* elementFromPoint(int x, int y) const;
PassRefPtr<Range> caretRangeFromPoint(int x, int y);
......
......@@ -1049,13 +1049,33 @@ HitTestResult EventHandler::hitTestResultAtPoint(const LayoutPoint& point, bool
hitType |= HitTestRequest::IgnoreClipping;
if (allowShadowContent)
hitType |= HitTestRequest::AllowShadowContent;
if (testScrollbars == ShouldHitTestScrollbars)
hitType |= HitTestRequest::TestChildFrameScrollBars;
// We always need to handle child frame content.
hitType |= HitTestRequest::AllowChildFrameContent;
m_frame->contentRenderer()->hitTest(HitTestRequest(hitType), result);
while (true) {
Node* n = result.innerNode();
if (!result.isOverWidget() || !n || !n->renderer() || !n->renderer()->isWidget())
break;
RenderWidget* renderWidget = toRenderWidget(n->renderer());
Widget* widget = renderWidget->widget();
if (!widget || !widget->isFrameView())
break;
Frame* frame = static_cast<HTMLFrameElementBase*>(n)->contentFrame();
if (!frame || !frame->contentRenderer())
break;
FrameView* view = static_cast<FrameView*>(widget);
LayoutPoint widgetPoint(result.localPoint().x() + view->scrollX() - renderWidget->borderLeft() - renderWidget->paddingLeft(),
result.localPoint().y() + view->scrollY() - renderWidget->borderTop() - renderWidget->paddingTop());
HitTestResult widgetHitTestResult(widgetPoint, padding.height(), padding.width(), padding.height(), padding.width());
frame->contentRenderer()->hitTest(HitTestRequest(hitType), widgetHitTestResult);
result = widgetHitTestResult;
if (testScrollbars == ShouldHitTestScrollbars) {
Scrollbar* eventScrollbar = view->scrollbarAtPoint(roundedIntPoint(point));
if (eventScrollbar)
result.setScrollbar(eventScrollbar);
}
}
// If our HitTestResult is not visible, then we started hit testing too far down the frame chain.
// Another hit test at the main frame level should get us the correct visible result.
Frame* resultFrame = result.innerNonSharedNode() ? result.innerNonSharedNode()->document()->frame() : 0;
......
......@@ -35,10 +35,7 @@ public:
IgnoreClipping = 1 << 5,
SVGClipContent = 1 << 6,
TouchEvent = 1 << 7,
AllowShadowContent = 1 << 8,
AllowChildFrameContent = 1 << 9,
ChildFrameHitTest = 1 << 10,
TestChildFrameScrollBars = 1 << 11
AllowShadowContent = 1 << 8
};
typedef unsigned HitTestRequestType;
......@@ -57,9 +54,6 @@ public:
bool touchEvent() const { return m_requestType & TouchEvent; }
bool mouseEvent() const { return !touchEvent(); }
bool allowsShadowContent() const { return m_requestType & AllowShadowContent; }
bool allowsChildFrameContent() const { return m_requestType & AllowChildFrameContent; }
bool isChildFrameHitTest() const { return m_requestType & ChildFrameHitTest; }
bool shouldTestChildFrameScrollBars() const { return m_requestType & TestChildFrameScrollBars; }
// Convenience functions
bool touchMove() const { return move() && touchEvent(); }
......
......@@ -104,7 +104,7 @@ HitTestLocation::HitTestLocation(const HitTestLocation& other, const LayoutSize&
, m_boundingBox(other.m_boundingBox)
, m_transformedPoint(other.m_transformedPoint)
, m_transformedRect(other.m_transformedRect)
, m_region(region ? region : other.m_region)
, m_region(region)
, m_isRectBased(other.m_isRectBased)
, m_isRectilinear(other.m_isRectilinear)
{
......
......@@ -55,7 +55,7 @@ public:
// Pass non-zero padding values to perform a rect-based hit test.
HitTestLocation(const LayoutPoint& centerPoint, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding);
// Make a copy the HitTestLocation in a new region by applying given offset to internal point and area.