+2007-12-03 Dan Bernstein <mitz@apple.com>
+
+ Reviewed by Dave Hyatt.
+
+ - fix <rdar://problem/5346452> Resize event doesn't fire on body element inside a frame
+
+ Test: fast/events/resize-subframe.html
+
+ * page/FrameView.cpp:
+ (WebCore::FrameViewPrivate::FrameViewPrivate): Added a timer used when
+ deferring tasks that need to be done after layout.
+ (WebCore::FrameViewPrivate::reset):
+ (WebCore::FrameView::~FrameView):
+ (WebCore::FrameView::layout): Moved the updating of widget positions,
+ loading plug-ins and sending events queued up during layout into
+ performPostLayoutTasks(). performPostLayoutTasks() is called after
+ layout unless the layout was triggered by a previous layout's post-
+ layout tasks. In the latter case, performPostLayoutTasks() is scheduled
+ to run later.
+ (WebCore::FrameView::performPostLayoutTasks): Performs work that needs
+ to be done after layout but which can result in arbitrary code
+ execution and therefore may re-invalidate the layout. This includes
+ updating widget positions, loading plug-ins, and dispatching layout-
+ related DOM events (scroll, overflow and resize).
+ (WebCore::FrameView::postLayoutTimerFired):
+ (WebCore::FrameView::dispatchScheduledEvents):
+ * page/FrameView.h:
+ * page/mac/WebCoreFrameBridge.h:
+ * page/mac/WebCoreFrameBridge.mm: Removed -sendResizeEvent since this
+ is handled by FrameView now.
+
2007-12-03 Rob Buis <buis@kde.org>
Reviewed by Darin.
: m_slowRepaintObjectCount(0)
, layoutTimer(view, &FrameView::layoutTimerFired)
, layoutRoot(0)
+ , postLayoutTasksTimer(view, &FrameView::postLayoutTimerFired)
, m_mediaType("screen")
, m_enqueueEvents(0)
, m_overflowStatusDirty(true)
midLayout = false;
layoutCount = 0;
nestedLayoutCount = 0;
+ postLayoutTasksTimer.stop();
firstLayout = true;
repaintRects.clear();
m_wasScrolledByUser = false;
+ lastLayoutSize = IntSize();
}
bool doFullRepaint;
bool midLayout;
int layoutCount;
unsigned nestedLayoutCount;
+ Timer<FrameView> postLayoutTasksTimer;
bool firstLayout;
bool needToInitScrollbars;
bool isTransparent;
Color baseBackgroundColor;
+ IntSize lastLayoutSize;
// Used by objects during layout to communicate repaints that need to take place only
// after all layout has been completed.
FrameView::~FrameView()
{
+ if (d->postLayoutTasksTimer.isActive()) {
+ d->postLayoutTasksTimer.stop();
+ d->m_scheduledEvents.clear();
+ d->m_enqueueEvents = 0;
+ }
+
resetScrollbars();
ASSERT(m_refCount == 0);
d->layoutSchedulingEnabled = false;
+ if (!d->nestedLayoutCount && d->postLayoutTasksTimer.isActive()) {
+ // This is a new top-level layout. If there are any remaining tasks from the previous
+ // layout, finish them now.
+ d->postLayoutTasksTimer.stop();
+ performPostLayoutTasks();
+ }
+
// Always ensure our style info is up-to-date. This can happen in situations where
// the layout beats any sort of style recalc update that needs to occur.
if (document->hasChangedChild())
bool subtree = d->layoutRoot;
// If there is only one ref to this view left, then its going to be destroyed as soon as we exit,
- // so there's no point to continuiing to layout
+ // so there's no point to continuing to layout
if (protector->hasOneRef())
return;
ScrollbarMode hMode = d->hmode;
ScrollbarMode vMode = d->vmode;
-
+
if (!subtree) {
RenderObject* rootRenderer = document->documentElement() ? document->documentElement()->renderer() : 0;
if (document->isHTMLDocument()) {
if (d->firstLayout) {
d->firstLayout = false;
didFirstLayout = true;
+ d->lastLayoutSize = IntSize(width(), height());
// Set the initial vMode to AlwaysOn if we're auto.
if (vMode == ScrollbarAuto)
}
RenderLayer* layer = root->enclosingLayer();
-
- pauseScheduledEvents();
+
+ if (!d->postLayoutTasksTimer.isActive())
+ pauseScheduledEvents();
if (subtree)
root->view()->pushLayoutState(root);
m_frame->invalidateSelection();
- d->layoutSchedulingEnabled=true;
+ d->layoutSchedulingEnabled = true;
if (!subtree && !static_cast<RenderView*>(root)->printing())
adjustViewSize();
// Now update the positions of all layers.
layer->updateLayerPositions(d->doFullRepaint);
- // We update our widget positions right after doing a layout.
- if (!subtree)
- static_cast<RenderView*>(root)->updateWidgetPositions();
-
// FIXME: Could optimize this and have objects removed from this list
// if they ever do full repaints.
Vector<RenderObject::RepaintInfo>::iterator end = d->repaintRects.end();
updateOverflowStatus(visibleWidth() < contentsWidth(),
visibleHeight() < contentsHeight());
- if (m_widgetUpdateSet && d->nestedLayoutCount == 1) {
- Vector<RenderPartObject*> objectVector;
- copyToVector(*m_widgetUpdateSet, objectVector);
- size_t size = objectVector.size();
- for (size_t i = 0; i < size; ++i) {
- RenderPartObject* object = objectVector[i];
- object->updateWidget(false);
-
- // updateWidget() can destroy the RenderPartObject, so we need to make sure it's
- // alive by checking if it's still in m_widgetUpdateSet.
- if (m_widgetUpdateSet->contains(object))
- object->updateWidgetPosition();
+ if (!d->postLayoutTasksTimer.isActive()) {
+ // Calls resumeScheduledEvents()
+ performPostLayoutTasks();
+
+ if (needsLayout()) {
+ // Post-layout widget updates or an event handler made us need layout again.
+ // Lay out again, but this time defer widget updates and event dispatch until after
+ // we return.
+ d->postLayoutTasksTimer.startOneShot(0);
+ pauseScheduledEvents();
+ layout();
}
- m_widgetUpdateSet->clear();
}
- // Allow events scheduled during layout to fire
- resumeScheduledEvents();
-
d->nestedLayoutCount--;
}
ASSERT(d->m_scheduledEvents.isEmpty() || d->m_enqueueEvents);
}
+void FrameView::performPostLayoutTasks()
+{
+ RenderView* root = static_cast<RenderView*>(m_frame->document()->renderer());
+
+ root->updateWidgetPositions();
+ if (m_widgetUpdateSet && d->nestedLayoutCount <= 1) {
+ Vector<RenderPartObject*> objectVector;
+ copyToVector(*m_widgetUpdateSet, objectVector);
+ size_t size = objectVector.size();
+ for (size_t i = 0; i < size; ++i) {
+ RenderPartObject* object = objectVector[i];
+ object->updateWidget(false);
+
+ // updateWidget() can destroy the RenderPartObject, so we need to make sure it's
+ // alive by checking if it's still in m_widgetUpdateSet.
+ if (m_widgetUpdateSet->contains(object))
+ object->updateWidgetPosition();
+ }
+ m_widgetUpdateSet->clear();
+ }
+
+ resumeScheduledEvents();
+
+ if (!root->printing()) {
+ IntSize currentSize = IntSize(width(), height());
+ bool resized = !d->firstLayout && currentSize != d->lastLayoutSize;
+ d->lastLayoutSize = currentSize;
+ if (resized)
+ m_frame->sendResizeEvent();
+ }
+}
+
+void FrameView::postLayoutTimerFired(Timer<FrameView>*)
+{
+ performPostLayoutTasks();
+}
+
void FrameView::updateOverflowStatus(bool horizontalOverflow, bool verticalOverflow)
{
if (!d->m_viewportRenderer)
{
if (d->m_scheduledEvents.isEmpty())
return;
-
+
Vector<ScheduledEvent*> scheduledEventsCopy = d->m_scheduledEvents;
d->m_scheduledEvents.clear();
ec, scheduledEvent->m_tempEvent);
delete scheduledEvent;
- }
+ }
}
IntRect FrameView::windowClipRect() const