|
1 /* |
|
2 * Copyright (C) 1998, 1999 Torben Weis <weis@kde.org> |
|
3 * 1999 Lars Knoll <knoll@kde.org> |
|
4 * 1999 Antti Koivisto <koivisto@kde.org> |
|
5 * 2000 Dirk Mueller <mueller@kde.org> |
|
6 * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. |
|
7 * (C) 2006 Graham Dennis (graham.dennis@gmail.com) |
|
8 * (C) 2006 Alexey Proskuryakov (ap@nypop.com) |
|
9 * |
|
10 * This library is free software; you can redistribute it and/or |
|
11 * modify it under the terms of the GNU Library General Public |
|
12 * License as published by the Free Software Foundation; either |
|
13 * version 2 of the License, or (at your option) any later version. |
|
14 * |
|
15 * This library is distributed in the hope that it will be useful, |
|
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
18 * Library General Public License for more details. |
|
19 * |
|
20 * You should have received a copy of the GNU Library General Public License |
|
21 * along with this library; see the file COPYING.LIB. If not, write to |
|
22 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
|
23 * Boston, MA 02110-1301, USA. |
|
24 */ |
|
25 |
|
26 #include "config.h" |
|
27 #include "FrameView.h" |
|
28 #include "Settings.h" |
|
29 |
|
30 #include "AXObjectCache.h" |
|
31 #include "EventHandler.h" |
|
32 #include "FloatRect.h" |
|
33 #include "Frame.h" |
|
34 #include "FrameLoader.h" |
|
35 #include "FrameLoaderClient.h" |
|
36 #include "GraphicsContext.h" |
|
37 #include "HTMLDocument.h" |
|
38 #include "HTMLFrameSetElement.h" |
|
39 #include "HTMLNames.h" |
|
40 #include "OverflowEvent.h" |
|
41 #include "RenderPart.h" |
|
42 #include "RenderPartObject.h" |
|
43 #include "RenderTheme.h" |
|
44 #include "RenderView.h" |
|
45 |
|
46 #if PLATFORM(SYMBIAN) |
|
47 #include "WebCoreFrameBridge.h" |
|
48 #include "FrameTree.h" |
|
49 #include "oom.h" |
|
50 #endif |
|
51 |
|
52 namespace WebCore { |
|
53 |
|
54 using namespace HTMLNames; |
|
55 |
|
56 struct ScheduledEvent { |
|
57 RefPtr<Event> m_event; |
|
58 RefPtr<EventTargetNode> m_eventTarget; |
|
59 bool m_tempEvent; |
|
60 }; |
|
61 |
|
62 class FrameViewPrivate { |
|
63 public: |
|
64 FrameViewPrivate(FrameView* view) |
|
65 : m_slowRepaintObjectCount(0) |
|
66 , layoutTimer(view, &FrameView::layoutTimerFired) |
|
67 , m_mediaType("screen") |
|
68 , m_enqueueEvents(0) |
|
69 , m_overflowStatusDirty(true) |
|
70 , m_viewportRenderer(0) |
|
71 , m_wasScrolledByUser(false) |
|
72 , m_inProgrammaticScroll(false) |
|
73 { |
|
74 isTransparent = false; |
|
75 baseBackgroundColor = Color::white; |
|
76 vmode = hmode = ScrollbarAuto; |
|
77 needToInitScrollbars = true; |
|
78 reset(); |
|
79 } |
|
80 void reset() |
|
81 { |
|
82 useSlowRepaints = false; |
|
83 borderX = 30; |
|
84 borderY = 30; |
|
85 layoutTimer.stop(); |
|
86 layoutRoot = 0; |
|
87 delayedLayout = false; |
|
88 doFullRepaint = true; |
|
89 layoutSchedulingEnabled = true; |
|
90 midLayout = false; |
|
91 layoutCount = 0; |
|
92 nestedLayoutCount = 0; |
|
93 firstLayout = true; |
|
94 repaintRects.clear(); |
|
95 m_wasScrolledByUser = false; |
|
96 } |
|
97 |
|
98 bool doFullRepaint; |
|
99 |
|
100 ScrollbarMode vmode; |
|
101 ScrollbarMode hmode; |
|
102 bool useSlowRepaints; |
|
103 unsigned m_slowRepaintObjectCount; |
|
104 |
|
105 int borderX, borderY; |
|
106 |
|
107 Timer<FrameView> layoutTimer; |
|
108 bool delayedLayout; |
|
109 RefPtr<Node> layoutRoot; |
|
110 |
|
111 bool layoutSchedulingEnabled; |
|
112 bool midLayout; |
|
113 int layoutCount; |
|
114 unsigned nestedLayoutCount; |
|
115 |
|
116 bool firstLayout; |
|
117 bool needToInitScrollbars; |
|
118 bool isTransparent; |
|
119 Color baseBackgroundColor; |
|
120 |
|
121 // Used by objects during layout to communicate repaints that need to take place only |
|
122 // after all layout has been completed. |
|
123 Vector<RenderObject::RepaintInfo> repaintRects; |
|
124 |
|
125 String m_mediaType; |
|
126 |
|
127 unsigned m_enqueueEvents; |
|
128 Vector<ScheduledEvent*> m_scheduledEvents; |
|
129 |
|
130 bool m_overflowStatusDirty; |
|
131 bool horizontalOverflow; |
|
132 bool m_verticalOverflow; |
|
133 RenderObject* m_viewportRenderer; |
|
134 |
|
135 bool m_wasScrolledByUser; |
|
136 bool m_inProgrammaticScroll; |
|
137 }; |
|
138 |
|
139 FrameView::FrameView(Frame* frame) |
|
140 : m_refCount(1) |
|
141 , m_frame(frame) |
|
142 , d(new FrameViewPrivate(this)) |
|
143 { |
|
144 init(); |
|
145 show(); |
|
146 } |
|
147 |
|
148 FrameView::~FrameView() |
|
149 { |
|
150 resetScrollbars(); |
|
151 |
|
152 ASSERT(m_refCount == 0); |
|
153 ASSERT(d->m_scheduledEvents.isEmpty() && !d->m_enqueueEvents); |
|
154 |
|
155 if (m_frame) { |
|
156 ASSERT(m_frame->view() != this || !m_frame->document() || !m_frame->document()->renderer()); |
|
157 RenderPart* renderer = m_frame->ownerRenderer(); |
|
158 if (renderer && renderer->widget() == this) |
|
159 renderer->setWidget(0); |
|
160 } |
|
161 |
|
162 delete d; |
|
163 d = 0; |
|
164 } |
|
165 |
|
166 bool FrameView::isFrameView() const |
|
167 { |
|
168 return true; |
|
169 } |
|
170 |
|
171 void FrameView::clearFrame() |
|
172 { |
|
173 m_frame = 0; |
|
174 } |
|
175 |
|
176 void FrameView::resetScrollbars() |
|
177 { |
|
178 // Reset the document's scrollbars back to our defaults before we yield the floor. |
|
179 d->firstLayout = true; |
|
180 suppressScrollbars(true); |
|
181 ScrollView::setVScrollbarMode(d->vmode); |
|
182 ScrollView::setHScrollbarMode(d->hmode); |
|
183 suppressScrollbars(false); |
|
184 } |
|
185 |
|
186 void FrameView::init() |
|
187 { |
|
188 m_margins = IntSize(-1, -1); // undefined |
|
189 m_size = IntSize(); |
|
190 } |
|
191 |
|
192 void FrameView::clear() |
|
193 { |
|
194 setStaticBackground(false); |
|
195 |
|
196 d->reset(); |
|
197 |
|
198 if (m_frame) |
|
199 if (RenderPart* renderer = m_frame->ownerRenderer()) |
|
200 renderer->viewCleared(); |
|
201 |
|
202 suppressScrollbars(true); |
|
203 } |
|
204 |
|
205 bool FrameView::didFirstLayout() const |
|
206 { |
|
207 return !d->firstLayout; |
|
208 } |
|
209 |
|
210 void FrameView::initScrollbars() |
|
211 { |
|
212 if (!d->needToInitScrollbars) |
|
213 return; |
|
214 d->needToInitScrollbars = false; |
|
215 setScrollbarsMode(hScrollbarMode()); |
|
216 } |
|
217 |
|
218 void FrameView::setMarginWidth(int w) |
|
219 { |
|
220 // make it update the rendering area when set |
|
221 m_margins.setWidth(w); |
|
222 } |
|
223 |
|
224 void FrameView::setMarginHeight(int h) |
|
225 { |
|
226 // make it update the rendering area when set |
|
227 m_margins.setHeight(h); |
|
228 } |
|
229 |
|
230 void FrameView::adjustViewSize() |
|
231 { |
|
232 ASSERT(m_frame->view() == this); |
|
233 RenderView* root = static_cast<RenderView*>(m_frame->renderer()); |
|
234 if (!root) |
|
235 return; |
|
236 resizeContents(root->overflowWidth(), root->overflowHeight()); |
|
237 } |
|
238 |
|
239 void FrameView::applyOverflowToViewport(RenderObject* o, ScrollbarMode& hMode, ScrollbarMode& vMode) |
|
240 { |
|
241 // Handle the overflow:hidden/scroll case for the body/html elements. WinIE treats |
|
242 // overflow:hidden and overflow:scroll on <body> as applying to the document's |
|
243 // scrollbars. The CSS2.1 draft states that HTML UAs should use the <html> or <body> element and XML/XHTML UAs should |
|
244 // use the root element. |
|
245 switch (o->style()->overflowX()) { |
|
246 case OHIDDEN: |
|
247 hMode = ScrollbarAlwaysOff; |
|
248 break; |
|
249 case OSCROLL: |
|
250 hMode = ScrollbarAlwaysOn; |
|
251 break; |
|
252 case OAUTO: |
|
253 hMode = ScrollbarAuto; |
|
254 break; |
|
255 default: |
|
256 // Don't set it at all. |
|
257 ; |
|
258 } |
|
259 |
|
260 switch (o->style()->overflowY()) { |
|
261 case OHIDDEN: |
|
262 vMode = ScrollbarAlwaysOff; |
|
263 break; |
|
264 case OSCROLL: |
|
265 vMode = ScrollbarAlwaysOn; |
|
266 break; |
|
267 case OAUTO: |
|
268 vMode = ScrollbarAuto; |
|
269 break; |
|
270 default: |
|
271 // Don't set it at all. |
|
272 ; |
|
273 } |
|
274 |
|
275 d->m_viewportRenderer = o; |
|
276 } |
|
277 |
|
278 int FrameView::layoutCount() const |
|
279 { |
|
280 return d->layoutCount; |
|
281 } |
|
282 |
|
283 bool FrameView::needsFullRepaint() const |
|
284 { |
|
285 return d->doFullRepaint; |
|
286 } |
|
287 |
|
288 void FrameView::addRepaintInfo(RenderObject* o, const IntRect& r) |
|
289 { |
|
290 d->repaintRects.append(RenderObject::RepaintInfo(o, r)); |
|
291 } |
|
292 |
|
293 Node* FrameView::layoutRoot() const |
|
294 { |
|
295 return layoutPending() ? 0 : d->layoutRoot.get(); |
|
296 } |
|
297 |
|
298 void FrameView::layout(bool allowSubtree) |
|
299 { |
|
300 #if PLATFORM(SYMBIAN) |
|
301 OOM_PRE_CHECK(1024*1024*2, 0, "FrameView::layout") |
|
302 #endif |
|
303 if (d->midLayout) |
|
304 return; |
|
305 |
|
306 d->layoutTimer.stop(); |
|
307 d->delayedLayout = false; |
|
308 |
|
309 // Protect the view from being deleted during layout (in recalcStyle) |
|
310 RefPtr<FrameView> protector(this); |
|
311 |
|
312 if (!m_frame) { |
|
313 // FIXME: Do we need to set m_size.width here? |
|
314 // FIXME: Should we set m_size.height here too? |
|
315 m_size.setWidth(visibleWidth()); |
|
316 return; |
|
317 } |
|
318 |
|
319 // we shouldn't enter layout() while painting |
|
320 ASSERT(!m_frame->isPainting()); |
|
321 if (m_frame->isPainting()) |
|
322 return; |
|
323 |
|
324 if (!allowSubtree && d->layoutRoot) { |
|
325 if (d->layoutRoot->renderer()) |
|
326 d->layoutRoot->renderer()->markContainingBlocksForLayout(false); |
|
327 d->layoutRoot = 0; |
|
328 } |
|
329 |
|
330 |
|
331 ASSERT(m_frame->view() == this); |
|
332 |
|
333 Document* document = m_frame->document(); |
|
334 if (!document) { |
|
335 // FIXME: Should we set m_size.height here too? |
|
336 m_size.setWidth(visibleWidth()); |
|
337 return; |
|
338 } |
|
339 |
|
340 d->layoutSchedulingEnabled = false; |
|
341 |
|
342 // Always ensure our style info is up-to-date. This can happen in situations where |
|
343 // the layout beats any sort of style recalc update that needs to occur. |
|
344 if (document->hasChangedChild()) |
|
345 document->recalcStyle(); |
|
346 |
|
347 bool subtree = d->layoutRoot; |
|
348 Node* rootNode = subtree ? d->layoutRoot.get() : document; |
|
349 |
|
350 // If there is only one ref to this view left, then its going to be destroyed as soon as we exit, |
|
351 // so there's no point to continuiing to layout |
|
352 if (protector->hasOneRef()) |
|
353 return; |
|
354 |
|
355 RenderObject* root = rootNode->renderer(); |
|
356 if (!root) { |
|
357 // FIXME: Do we need to set m_size here? |
|
358 d->layoutSchedulingEnabled = true; |
|
359 return; |
|
360 } |
|
361 |
|
362 d->nestedLayoutCount++; |
|
363 |
|
364 ScrollbarMode hMode = d->hmode; |
|
365 ScrollbarMode vMode = d->vmode; |
|
366 |
|
367 if (!subtree) { |
|
368 Document* document = static_cast<Document*>(rootNode); |
|
369 RenderObject* rootRenderer = document->documentElement() ? document->documentElement()->renderer() : 0; |
|
370 if (document->isHTMLDocument()) { |
|
371 Node* body = static_cast<HTMLDocument*>(document)->body(); |
|
372 if (body && body->renderer()) { |
|
373 if (body->hasTagName(framesetTag) && !m_frame->settings()->flatFrameSetLayoutEnabled()) { |
|
374 body->renderer()->setChildNeedsLayout(true); |
|
375 vMode = ScrollbarAlwaysOff; |
|
376 hMode = ScrollbarAlwaysOff; |
|
377 } else if (body->hasTagName(bodyTag)) { |
|
378 if (!d->firstLayout && m_size.height() != visibleHeight() |
|
379 && static_cast<RenderBox*>(body->renderer())->stretchesToViewHeight()) |
|
380 body->renderer()->setChildNeedsLayout(true); |
|
381 // It's sufficient to just check the X overflow, |
|
382 // since it's illegal to have visible in only one direction. |
|
383 RenderObject* o = rootRenderer->style()->overflowX() == OVISIBLE |
|
384 ? body->renderer() : rootRenderer; |
|
385 applyOverflowToViewport(o, hMode, vMode); // Only applies to HTML UAs, not to XML/XHTML UAs |
|
386 } |
|
387 } |
|
388 } else if (rootRenderer) |
|
389 applyOverflowToViewport(rootRenderer, hMode, vMode); // XML/XHTML UAs use the root element. |
|
390 #ifdef INSTRUMENT_LAYOUT_SCHEDULING |
|
391 if (d->firstLayout && !document->ownerElement()) |
|
392 printf("Elapsed time before first layout: %d\n", document->elapsedTime()); |
|
393 #endif |
|
394 } |
|
395 |
|
396 d->doFullRepaint = !subtree && (d->firstLayout || static_cast<RenderView*>(root)->printing()); |
|
397 d->repaintRects.clear(); |
|
398 |
|
399 bool didFirstLayout = false; |
|
400 if (!subtree) { |
|
401 // Now set our scrollbar state for the layout. |
|
402 ScrollbarMode currentHMode = hScrollbarMode(); |
|
403 ScrollbarMode currentVMode = vScrollbarMode(); |
|
404 |
|
405 if (d->firstLayout || (hMode != currentHMode || vMode != currentVMode)) { |
|
406 suppressScrollbars(true); |
|
407 if (d->firstLayout) { |
|
408 d->firstLayout = false; |
|
409 didFirstLayout = true; |
|
410 |
|
411 // Set the initial vMode to AlwaysOn if we're auto. |
|
412 if (vMode == ScrollbarAuto) |
|
413 ScrollView::setVScrollbarMode(ScrollbarAlwaysOn); // This causes a vertical scrollbar to appear. |
|
414 // Set the initial hMode to AlwaysOff if we're auto. |
|
415 if (hMode == ScrollbarAuto) |
|
416 ScrollView::setHScrollbarMode(ScrollbarAlwaysOff); // This causes a horizontal scrollbar to disappear. |
|
417 } |
|
418 |
|
419 if (hMode == vMode) |
|
420 ScrollView::setScrollbarsMode(hMode); |
|
421 else { |
|
422 ScrollView::setHScrollbarMode(hMode); |
|
423 ScrollView::setVScrollbarMode(vMode); |
|
424 } |
|
425 |
|
426 suppressScrollbars(false, true); |
|
427 } |
|
428 |
|
429 IntSize oldSize = m_size; |
|
430 |
|
431 m_size = IntSize(visibleWidth(), visibleHeight()); |
|
432 |
|
433 if (oldSize != m_size) |
|
434 d->doFullRepaint = true; |
|
435 } |
|
436 |
|
437 RenderLayer* layer = root->enclosingLayer(); |
|
438 |
|
439 pauseScheduledEvents(); |
|
440 |
|
441 if (subtree) |
|
442 root->view()->pushLayoutState(root); |
|
443 d->midLayout = true; |
|
444 root->layout(); |
|
445 d->midLayout = false; |
|
446 if (subtree) |
|
447 root->view()->popLayoutState(); |
|
448 d->layoutRoot = 0; |
|
449 |
|
450 m_frame->invalidateSelection(); |
|
451 |
|
452 d->layoutSchedulingEnabled=true; |
|
453 |
|
454 if (!subtree && !static_cast<RenderView*>(root)->printing()) |
|
455 adjustViewSize(); |
|
456 |
|
457 // Now update the positions of all layers. |
|
458 layer->updateLayerPositions(d->doFullRepaint); |
|
459 |
|
460 // We update our widget positions right after doing a layout. |
|
461 if (!subtree) |
|
462 static_cast<RenderView*>(root)->updateWidgetPositions(); |
|
463 |
|
464 // FIXME: Could optimize this and have objects removed from this list |
|
465 // if they ever do full repaints. |
|
466 Vector<RenderObject::RepaintInfo>::iterator end = d->repaintRects.end(); |
|
467 for (Vector<RenderObject::RepaintInfo>::iterator it = d->repaintRects.begin(); it != end; ++it) |
|
468 it->m_object->repaintRectangle(it->m_repaintRect); |
|
469 d->repaintRects.clear(); |
|
470 |
|
471 d->layoutCount++; |
|
472 |
|
473 #if PLATFORM(MAC) |
|
474 if (AXObjectCache::accessibilityEnabled()) |
|
475 root->document()->axObjectCache()->postNotificationToElement(root, "AXLayoutComplete"); |
|
476 #endif |
|
477 updateDashboardRegions(); |
|
478 #if PLATFORM(SYMBIAN) |
|
479 // Update focusable rect lists in main frame |
|
480 if (m_frame.get()->bridge()) { |
|
481 m_frame.get()->bridge()->updateFocusableRectList(); |
|
482 m_frame.get()->bridge()->updateThumbnail(); |
|
483 } |
|
484 #endif |
|
485 |
|
486 if (didFirstLayout) |
|
487 m_frame->loader()->didFirstLayout(); |
|
488 |
|
489 ASSERT(!root->needsLayout()); |
|
490 |
|
491 setStaticBackground(useSlowRepaints()); |
|
492 |
|
493 if (document->hasListenerType(Document::OVERFLOWCHANGED_LISTENER)) |
|
494 updateOverflowStatus(visibleWidth() < contentsWidth(), |
|
495 visibleHeight() < contentsHeight()); |
|
496 |
|
497 if (m_widgetUpdateSet && d->nestedLayoutCount == 1) { |
|
498 Vector<RenderPartObject*> objectVector; |
|
499 copyToVector(*m_widgetUpdateSet, objectVector); |
|
500 size_t size = objectVector.size(); |
|
501 for (size_t i = 0; i < size; ++i) { |
|
502 RenderPartObject* object = objectVector[i]; |
|
503 object->updateWidget(false); |
|
504 |
|
505 // updateWidget() can destroy the RenderPartObject, so we need to make sure it's |
|
506 // alive by checking if it's still in m_widgetUpdateSet. |
|
507 if (m_widgetUpdateSet->contains(object)) |
|
508 object->updateWidgetPosition(); |
|
509 } |
|
510 m_widgetUpdateSet->clear(); |
|
511 } |
|
512 |
|
513 // Allow events scheduled during layout to fire |
|
514 resumeScheduledEvents(); |
|
515 |
|
516 d->nestedLayoutCount--; |
|
517 #if PLATFORM(SYMBIAN) |
|
518 OOM_POST_CHECK_FAILED(unscheduleRelayout(); return;) |
|
519 #endif |
|
520 } |
|
521 |
|
522 void FrameView::addWidgetToUpdate(RenderPartObject* object) |
|
523 { |
|
524 if (!m_widgetUpdateSet) |
|
525 m_widgetUpdateSet.set(new HashSet<RenderPartObject*>); |
|
526 |
|
527 m_widgetUpdateSet->add(object); |
|
528 } |
|
529 |
|
530 void FrameView::removeWidgetToUpdate(RenderPartObject* object) |
|
531 { |
|
532 if (!m_widgetUpdateSet) |
|
533 return; |
|
534 |
|
535 m_widgetUpdateSet->remove(object); |
|
536 } |
|
537 |
|
538 // |
|
539 // Event Handling |
|
540 // |
|
541 ///////////////// |
|
542 |
|
543 bool FrameView::scrollTo(const IntRect& bounds) |
|
544 { |
|
545 int x, y, xe, ye; |
|
546 x = bounds.x(); |
|
547 y = bounds.y(); |
|
548 xe = bounds.right() - 1; |
|
549 ye = bounds.bottom() - 1; |
|
550 |
|
551 int deltax; |
|
552 int deltay; |
|
553 |
|
554 int curHeight = visibleHeight(); |
|
555 int curWidth = visibleWidth(); |
|
556 |
|
557 if (ye - y>curHeight-d->borderY) |
|
558 ye = y + curHeight - d->borderY; |
|
559 |
|
560 if (xe - x>curWidth-d->borderX) |
|
561 xe = x + curWidth - d->borderX; |
|
562 |
|
563 // is xpos of target left of the view's border? |
|
564 if (x < contentsX() + d->borderX) |
|
565 deltax = x - contentsX() - d->borderX; |
|
566 // is xpos of target right of the view's right border? |
|
567 else if (xe + d->borderX > contentsX() + curWidth) |
|
568 deltax = xe + d->borderX - (contentsX() + curWidth); |
|
569 else |
|
570 deltax = 0; |
|
571 |
|
572 // is ypos of target above upper border? |
|
573 if (y < contentsY() + d->borderY) |
|
574 deltay = y - contentsY() - d->borderY; |
|
575 // is ypos of target below lower border? |
|
576 else if (ye + d->borderY > contentsY() + curHeight) |
|
577 deltay = ye + d->borderY - (contentsY() + curHeight); |
|
578 else |
|
579 deltay = 0; |
|
580 |
|
581 int maxx = curWidth - d->borderX; |
|
582 int maxy = curHeight - d->borderY; |
|
583 |
|
584 int scrollX = deltax > 0 ? (deltax > maxx ? maxx : deltax) : deltax == 0 ? 0 : (deltax > -maxx ? deltax : -maxx); |
|
585 int scrollY = deltay > 0 ? (deltay > maxy ? maxy : deltay) : deltay == 0 ? 0 : (deltay > -maxy ? deltay : -maxy); |
|
586 |
|
587 if (contentsX() + scrollX < 0) |
|
588 scrollX = -contentsX(); |
|
589 else if (contentsWidth() - visibleWidth() - contentsX() < scrollX) |
|
590 scrollX = contentsWidth() - visibleWidth() - contentsX(); |
|
591 |
|
592 if (contentsY() + scrollY < 0) |
|
593 scrollY = -contentsY(); |
|
594 else if (contentsHeight() - visibleHeight() - contentsY() < scrollY) |
|
595 scrollY = contentsHeight() - visibleHeight() - contentsY(); |
|
596 |
|
597 scrollBy(scrollX, scrollY); |
|
598 |
|
599 // generate abs(scroll.) |
|
600 if (scrollX < 0) |
|
601 scrollX = -scrollX; |
|
602 if (scrollY < 0) |
|
603 scrollY = -scrollY; |
|
604 |
|
605 return scrollX != maxx && scrollY != maxy; |
|
606 } |
|
607 |
|
608 void FrameView::setMediaType(const String& mediaType) |
|
609 { |
|
610 d->m_mediaType = mediaType; |
|
611 } |
|
612 |
|
613 String FrameView::mediaType() const |
|
614 { |
|
615 // See if we have an override type. |
|
616 String overrideType = m_frame->loader()->client()->overrideMediaType(); |
|
617 if (!overrideType.isNull()) |
|
618 return overrideType; |
|
619 return d->m_mediaType; |
|
620 } |
|
621 |
|
622 bool FrameView::useSlowRepaints() const |
|
623 { |
|
624 return d->useSlowRepaints || d->m_slowRepaintObjectCount > 0; |
|
625 } |
|
626 |
|
627 void FrameView::setUseSlowRepaints() |
|
628 { |
|
629 d->useSlowRepaints = true; |
|
630 setStaticBackground(true); |
|
631 } |
|
632 |
|
633 void FrameView::addSlowRepaintObject() |
|
634 { |
|
635 if (!d->m_slowRepaintObjectCount) |
|
636 setStaticBackground(true); |
|
637 d->m_slowRepaintObjectCount++; |
|
638 } |
|
639 |
|
640 void FrameView::removeSlowRepaintObject() |
|
641 { |
|
642 ASSERT(d->m_slowRepaintObjectCount > 0); |
|
643 d->m_slowRepaintObjectCount--; |
|
644 if (!d->m_slowRepaintObjectCount) |
|
645 setStaticBackground(d->useSlowRepaints); |
|
646 } |
|
647 |
|
648 void FrameView::setScrollbarsMode(ScrollbarMode mode) |
|
649 { |
|
650 d->vmode = mode; |
|
651 d->hmode = mode; |
|
652 |
|
653 ScrollView::setScrollbarsMode(mode); |
|
654 } |
|
655 |
|
656 void FrameView::setVScrollbarMode(ScrollbarMode mode) |
|
657 { |
|
658 d->vmode = mode; |
|
659 ScrollView::setVScrollbarMode(mode); |
|
660 } |
|
661 |
|
662 void FrameView::setHScrollbarMode(ScrollbarMode mode) |
|
663 { |
|
664 d->hmode = mode; |
|
665 ScrollView::setHScrollbarMode(mode); |
|
666 } |
|
667 |
|
668 void FrameView::restoreScrollbar() |
|
669 { |
|
670 suppressScrollbars(false); |
|
671 } |
|
672 |
|
673 void FrameView::scrollRectIntoViewRecursively(const IntRect& r) |
|
674 { |
|
675 if (frame()->prohibitsScrolling()) |
|
676 return; |
|
677 bool wasInProgrammaticScroll = d->m_inProgrammaticScroll; |
|
678 d->m_inProgrammaticScroll = true; |
|
679 ScrollView::scrollRectIntoViewRecursively(r); |
|
680 d->m_inProgrammaticScroll = wasInProgrammaticScroll; |
|
681 } |
|
682 |
|
683 void FrameView::setContentsPos(int x, int y) |
|
684 { |
|
685 if (frame()->prohibitsScrolling()) |
|
686 return; |
|
687 bool wasInProgrammaticScroll = d->m_inProgrammaticScroll; |
|
688 d->m_inProgrammaticScroll = true; |
|
689 ScrollView::setContentsPos(x, y); |
|
690 d->m_inProgrammaticScroll = wasInProgrammaticScroll; |
|
691 } |
|
692 |
|
693 void FrameView::repaintRectangle(const IntRect& r, bool immediate) |
|
694 { |
|
695 updateContents(r, immediate); |
|
696 } |
|
697 |
|
698 void FrameView::layoutTimerFired(Timer<FrameView>*) |
|
699 { |
|
700 #ifdef INSTRUMENT_LAYOUT_SCHEDULING |
|
701 if (m_frame->document() && !m_frame->document()->ownerElement()) |
|
702 printf("Layout timer fired at %d\n", m_frame->document()->elapsedTime()); |
|
703 #endif |
|
704 layout(); |
|
705 } |
|
706 |
|
707 void FrameView::scheduleRelayout() |
|
708 { |
|
709 ASSERT(m_frame->view() == this); |
|
710 |
|
711 if (d->layoutRoot) { |
|
712 if (d->layoutRoot->renderer()) |
|
713 d->layoutRoot->renderer()->markContainingBlocksForLayout(false); |
|
714 d->layoutRoot = 0; |
|
715 } |
|
716 if (!d->layoutSchedulingEnabled) |
|
717 return; |
|
718 |
|
719 if (!m_frame->document() || !m_frame->document()->shouldScheduleLayout()) |
|
720 return; |
|
721 |
|
722 // In flat frame layout mode the content of frame affects layout of the parent frames. |
|
723 // Invalidate also parent frame starting from the owner element of this frame. |
|
724 #if PLATFORM(SYMBIAN) |
|
725 if (m_frame->settings()->flatFrameSetLayoutEnabled() && m_frame->ownerRenderer() && m_frame->tree()->parent()->isFrameSet()) |
|
726 #else |
|
727 if (m_frame->settings()->flatFrameSetLayoutEnabled() && m_frame->ownerRenderer()) |
|
728 #endif |
|
729 m_frame->ownerRenderer()->setNeedsLayout(true, true); |
|
730 |
|
731 int delay = m_frame->document()->minimumLayoutDelay(); |
|
732 if (d->layoutTimer.isActive() && d->delayedLayout && !delay) |
|
733 unscheduleRelayout(); |
|
734 if (d->layoutTimer.isActive()) |
|
735 return; |
|
736 |
|
737 d->delayedLayout = delay != 0; |
|
738 |
|
739 #ifdef INSTRUMENT_LAYOUT_SCHEDULING |
|
740 if (!m_frame->document()->ownerElement()) |
|
741 printf("Scheduling layout for %d\n", delay); |
|
742 #endif |
|
743 |
|
744 d->layoutTimer.startOneShot(delay * 0.001); |
|
745 } |
|
746 |
|
747 void FrameView::scheduleRelayoutOfSubtree(Node* n) |
|
748 { |
|
749 ASSERT(m_frame->view() == this); |
|
750 |
|
751 if (!d->layoutSchedulingEnabled || (m_frame->document() |
|
752 && m_frame->document()->renderer() |
|
753 && m_frame->document()->renderer()->needsLayout())) { |
|
754 if (n->renderer()) |
|
755 n->renderer()->markContainingBlocksForLayout(false); |
|
756 return; |
|
757 } |
|
758 |
|
759 if (layoutPending()) { |
|
760 if (d->layoutRoot != n) { |
|
761 // Just do a full relayout |
|
762 if (d->layoutRoot && d->layoutRoot->renderer()) |
|
763 d->layoutRoot->renderer()->markContainingBlocksForLayout(false); |
|
764 d->layoutRoot = 0; |
|
765 if (n->renderer()) |
|
766 n->renderer()->markContainingBlocksForLayout(false); |
|
767 } |
|
768 } else { |
|
769 int delay = m_frame->document()->minimumLayoutDelay(); |
|
770 d->layoutRoot = n; |
|
771 d->delayedLayout = delay != 0; |
|
772 d->layoutTimer.startOneShot(delay * 0.001); |
|
773 } |
|
774 } |
|
775 |
|
776 bool FrameView::layoutPending() const |
|
777 { |
|
778 return d->layoutTimer.isActive(); |
|
779 } |
|
780 |
|
781 bool FrameView::needsLayout() const |
|
782 { |
|
783 // It is possible that our document will not have a body yet. If this is the case, |
|
784 // then we are not allowed to schedule layouts yet, so we won't be pending layout. |
|
785 if (!m_frame) |
|
786 return false; |
|
787 RenderView* root = static_cast<RenderView*>(m_frame->renderer()); |
|
788 Document * doc = m_frame->document(); |
|
789 // doc->hasChangedChild() condition can occur when using WebKit ObjC interface |
|
790 return layoutPending() || (root && root->needsLayout()) || d->layoutRoot || (doc && doc->hasChangedChild()); |
|
791 } |
|
792 |
|
793 void FrameView::setNeedsLayout() |
|
794 { |
|
795 if (m_frame->renderer()) |
|
796 m_frame->renderer()->setNeedsLayout(true); |
|
797 } |
|
798 |
|
799 void FrameView::unscheduleRelayout() |
|
800 { |
|
801 if (!d->layoutTimer.isActive()) |
|
802 return; |
|
803 |
|
804 #ifdef INSTRUMENT_LAYOUT_SCHEDULING |
|
805 if (m_frame->document() && !m_frame->document()->ownerElement()) |
|
806 printf("Layout timer unscheduled at %d\n", m_frame->document()->elapsedTime()); |
|
807 #endif |
|
808 |
|
809 d->layoutTimer.stop(); |
|
810 d->delayedLayout = false; |
|
811 } |
|
812 |
|
813 bool FrameView::isTransparent() const |
|
814 { |
|
815 return d->isTransparent; |
|
816 } |
|
817 |
|
818 void FrameView::setTransparent(bool isTransparent) |
|
819 { |
|
820 d->isTransparent = isTransparent; |
|
821 } |
|
822 |
|
823 Color FrameView::baseBackgroundColor() const |
|
824 { |
|
825 return d->baseBackgroundColor; |
|
826 } |
|
827 |
|
828 void FrameView::setBaseBackgroundColor(Color bc) |
|
829 { |
|
830 if (!bc.isValid()) |
|
831 bc = Color::white; |
|
832 d->baseBackgroundColor = bc; |
|
833 } |
|
834 |
|
835 void FrameView::scheduleEvent(PassRefPtr<Event> event, PassRefPtr<EventTargetNode> eventTarget, bool tempEvent) |
|
836 { |
|
837 if (!d->m_enqueueEvents) { |
|
838 ExceptionCode ec = 0; |
|
839 eventTarget->dispatchEvent(event, ec, tempEvent); |
|
840 return; |
|
841 } |
|
842 |
|
843 ScheduledEvent* scheduledEvent = new ScheduledEvent; |
|
844 scheduledEvent->m_event = event; |
|
845 scheduledEvent->m_eventTarget = eventTarget; |
|
846 scheduledEvent->m_tempEvent = tempEvent; |
|
847 d->m_scheduledEvents.append(scheduledEvent); |
|
848 } |
|
849 |
|
850 void FrameView::pauseScheduledEvents() |
|
851 { |
|
852 ASSERT(d->m_scheduledEvents.isEmpty() || d->m_enqueueEvents); |
|
853 d->m_enqueueEvents++; |
|
854 } |
|
855 |
|
856 void FrameView::resumeScheduledEvents() |
|
857 { |
|
858 d->m_enqueueEvents--; |
|
859 if (!d->m_enqueueEvents) |
|
860 dispatchScheduledEvents(); |
|
861 ASSERT(d->m_scheduledEvents.isEmpty() || d->m_enqueueEvents); |
|
862 } |
|
863 |
|
864 void FrameView::updateOverflowStatus(bool horizontalOverflow, bool verticalOverflow) |
|
865 { |
|
866 if (!d->m_viewportRenderer) |
|
867 return; |
|
868 |
|
869 if (d->m_overflowStatusDirty) { |
|
870 d->horizontalOverflow = horizontalOverflow; |
|
871 d->m_verticalOverflow = verticalOverflow; |
|
872 d->m_overflowStatusDirty = false; |
|
873 return; |
|
874 } |
|
875 |
|
876 bool horizontalOverflowChanged = (d->horizontalOverflow != horizontalOverflow); |
|
877 bool verticalOverflowChanged = (d->m_verticalOverflow != verticalOverflow); |
|
878 |
|
879 if (horizontalOverflowChanged || verticalOverflowChanged) { |
|
880 d->horizontalOverflow = horizontalOverflow; |
|
881 d->m_verticalOverflow = verticalOverflow; |
|
882 |
|
883 scheduleEvent(new OverflowEvent(horizontalOverflowChanged, horizontalOverflow, |
|
884 verticalOverflowChanged, verticalOverflow), |
|
885 EventTargetNodeCast(d->m_viewportRenderer->element()), true); |
|
886 } |
|
887 |
|
888 } |
|
889 |
|
890 void FrameView::dispatchScheduledEvents() |
|
891 { |
|
892 if (d->m_scheduledEvents.isEmpty()) |
|
893 return; |
|
894 |
|
895 Vector<ScheduledEvent*> scheduledEventsCopy = d->m_scheduledEvents; |
|
896 d->m_scheduledEvents.clear(); |
|
897 |
|
898 Vector<ScheduledEvent*>::iterator end = scheduledEventsCopy.end(); |
|
899 for (Vector<ScheduledEvent*>::iterator it = scheduledEventsCopy.begin(); it != end; ++it) { |
|
900 ScheduledEvent* scheduledEvent = *it; |
|
901 |
|
902 ExceptionCode ec = 0; |
|
903 |
|
904 // Only dispatch events to nodes that are in the document |
|
905 if (scheduledEvent->m_eventTarget->inDocument()) |
|
906 scheduledEvent->m_eventTarget->dispatchEvent(scheduledEvent->m_event, |
|
907 ec, scheduledEvent->m_tempEvent); |
|
908 |
|
909 delete scheduledEvent; |
|
910 } |
|
911 } |
|
912 |
|
913 IntRect FrameView::windowClipRect() const |
|
914 { |
|
915 return windowClipRect(true); |
|
916 } |
|
917 |
|
918 IntRect FrameView::windowClipRect(bool clipToContents) const |
|
919 { |
|
920 ASSERT(m_frame->view() == this); |
|
921 |
|
922 // Set our clip rect to be our contents. |
|
923 IntRect clipRect; |
|
924 if (clipToContents) |
|
925 clipRect = enclosingIntRect(visibleContentRect()); |
|
926 else |
|
927 clipRect = IntRect(contentsX(), contentsY(), width(), height()); |
|
928 clipRect = contentsToWindow(clipRect); |
|
929 |
|
930 if (!m_frame || !m_frame->document() || !m_frame->document()->ownerElement()) |
|
931 return clipRect; |
|
932 |
|
933 // Take our owner element and get the clip rect from the enclosing layer. |
|
934 Element* elt = m_frame->document()->ownerElement(); |
|
935 RenderLayer* layer = elt->renderer()->enclosingLayer(); |
|
936 // FIXME: layer should never be null, but sometimes seems to be anyway. |
|
937 if (!layer) |
|
938 return clipRect; |
|
939 FrameView* parentView = elt->document()->view(); |
|
940 clipRect.intersect(parentView->windowClipRectForLayer(layer, true)); |
|
941 return clipRect; |
|
942 } |
|
943 |
|
944 IntRect FrameView::windowClipRectForLayer(const RenderLayer* layer, bool clipToLayerContents) const |
|
945 { |
|
946 // If we have no layer, just return our window clip rect. |
|
947 if (!layer) |
|
948 return windowClipRect(); |
|
949 |
|
950 // Apply the clip from the layer. |
|
951 IntRect clipRect; |
|
952 if (clipToLayerContents) |
|
953 clipRect = layer->childrenClipRect(); |
|
954 else |
|
955 clipRect = layer->selfClipRect(); |
|
956 clipRect = contentsToWindow(clipRect); |
|
957 return intersection(clipRect, windowClipRect()); |
|
958 } |
|
959 |
|
960 void FrameView::updateDashboardRegions() |
|
961 { |
|
962 Document* doc = m_frame->document(); |
|
963 if (doc->hasDashboardRegions()) { |
|
964 Vector<DashboardRegionValue> newRegions; |
|
965 doc->renderer()->collectDashboardRegions(newRegions); |
|
966 doc->setDashboardRegions(newRegions); |
|
967 m_frame.get()->dashboardRegionsChanged(); |
|
968 } |
|
969 } |
|
970 |
|
971 void FrameView::updateControlTints() |
|
972 { |
|
973 // This is called when control tints are changed from aqua/graphite to clear and vice versa. |
|
974 // We do a "fake" paint, and when the theme gets a paint call, it can then do an invalidate. |
|
975 // This is only done if the theme supports control tinting. It's up to the theme and platform |
|
976 // to define when controls get the tint and to call this function when that changes. |
|
977 |
|
978 // Optimize the common case where we bring a window to the front while it's still empty. |
|
979 if (!m_frame || m_frame->loader()->url().isEmpty()) |
|
980 return; |
|
981 |
|
982 Document* doc = m_frame->document(); |
|
983 if (doc && theme()->supportsControlTints() && m_frame->renderer()) { |
|
984 doc->updateLayout(); // Ensure layout is up to date. |
|
985 PlatformGraphicsContext* const noContext = 0; |
|
986 GraphicsContext context(noContext); |
|
987 context.setUpdatingControlTints(true); |
|
988 m_frame->paint(&context, enclosingIntRect(visibleContentRect())); |
|
989 } |
|
990 } |
|
991 |
|
992 bool FrameView::wasScrolledByUser() const |
|
993 { |
|
994 return d->m_wasScrolledByUser; |
|
995 } |
|
996 |
|
997 void FrameView::setWasScrolledByUser(bool wasScrolledByUser) |
|
998 { |
|
999 if (d->m_inProgrammaticScroll) |
|
1000 return; |
|
1001 d->m_wasScrolledByUser = wasScrolledByUser; |
|
1002 } |
|
1003 |
|
1004 #if PLATFORM(WIN) || PLATFORM(GTK) |
|
1005 void FrameView::layoutIfNeededRecursive() |
|
1006 { |
|
1007 // We have to crawl our entire tree looking for any FrameViews that need |
|
1008 // layout and make sure they are up to date. |
|
1009 // Mac actually tests for intersection with the dirty region and tries not to |
|
1010 // update layout for frames that are outside the dirty region. Not only does this seem |
|
1011 // pointless (since those frames will have set a zero timer to layout anyway), but |
|
1012 // it is also incorrect, since if two frames overlap, the first could be excluded from the dirty |
|
1013 // region but then become included later by the second frame adding rects to the dirty region |
|
1014 // when it lays out. |
|
1015 |
|
1016 if (needsLayout()) |
|
1017 layout(); |
|
1018 |
|
1019 HashSet<Widget*>* viewChildren = children(); |
|
1020 HashSet<Widget*>::iterator end = viewChildren->end(); |
|
1021 for (HashSet<Widget*>::iterator current = viewChildren->begin(); current != end; ++current) |
|
1022 if ((*current)->isFrameView()) |
|
1023 static_cast<FrameView*>(*current)->layoutIfNeededRecursive(); |
|
1024 } |
|
1025 #endif |
|
1026 |
|
1027 } |