dba6ec9c254b3554f674585ed4d5f0601cd612b7
[WebKit-https.git] / Source / WebCore / page / Page.cpp
1 /*
2  * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All Rights Reserved.
3  * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #include "config.h"
21 #include "Page.h"
22
23 #include "AlternativeTextClient.h"
24 #include "AnimationController.h"
25 #include "BackForwardController.h"
26 #include "BackForwardList.h"
27 #include "Chrome.h"
28 #include "ChromeClient.h"
29 #include "ClientRectList.h"
30 #include "ContextMenuClient.h"
31 #include "ContextMenuController.h"
32 #include "DOMWindow.h"
33 #include "DocumentMarkerController.h"
34 #include "DocumentStyleSheetCollection.h"
35 #include "DragController.h"
36 #include "Editor.h"
37 #include "EditorClient.h"
38 #include "Event.h"
39 #include "EventNames.h"
40 #include "ExceptionCode.h"
41 #include "ExceptionCodePlaceholder.h"
42 #include "FileSystem.h"
43 #include "FocusController.h"
44 #include "Frame.h"
45 #include "FrameLoader.h"
46 #include "FrameLoaderClient.h"
47 #include "FrameSelection.h"
48 #include "FrameTree.h"
49 #include "FrameView.h"
50 #include "HTMLElement.h"
51 #include "HistoryController.h"
52 #include "HistoryItem.h"
53 #include "InspectorController.h"
54 #include "InspectorInstrumentation.h"
55 #include "Logging.h"
56 #include "MediaCanStartListener.h"
57 #include "Navigator.h"
58 #include "NetworkStateNotifier.h"
59 #include "PageActivityAssertionToken.h"
60 #include "PageCache.h"
61 #include "PageConsole.h"
62 #include "PageGroup.h"
63 #include "PageThrottler.h"
64 #include "PlugInClient.h"
65 #include "PluginData.h"
66 #include "PluginView.h"
67 #include "PointerLockController.h"
68 #include "ProgressTracker.h"
69 #include "RenderArena.h"
70 #include "RenderLayerCompositor.h"
71 #include "RenderTheme.h"
72 #include "RenderView.h"
73 #include "RenderWidget.h"
74 #include "RuntimeEnabledFeatures.h"
75 #include "SchemeRegistry.h"
76 #include "ScriptController.h"
77 #include "ScrollingCoordinator.h"
78 #include "Settings.h"
79 #include "SharedBuffer.h"
80 #include "StorageArea.h"
81 #include "StorageNamespace.h"
82 #include "StyleResolver.h"
83 #include "TextResourceDecoder.h"
84 #include "VisitedLinkState.h"
85 #include "VoidCallback.h"
86 #include "Widget.h"
87 #include <wtf/HashMap.h>
88 #include <wtf/RefCountedLeakCounter.h>
89 #include <wtf/StdLibExtras.h>
90 #include <wtf/text/Base64.h>
91 #include <wtf/text/StringHash.h>
92
93 namespace WebCore {
94
95 static HashSet<Page*>* allPages;
96
97 DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, pageCounter, ("Page"));
98
99 static void networkStateChanged(bool isOnLine)
100 {
101     Vector<RefPtr<Frame> > frames;
102     
103     // Get all the frames of all the pages in all the page groups
104     HashSet<Page*>::iterator end = allPages->end();
105     for (HashSet<Page*>::iterator it = allPages->begin(); it != end; ++it) {
106         for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree().traverseNext())
107             frames.append(frame);
108         InspectorInstrumentation::networkStateChanged(*it);
109     }
110
111     AtomicString eventName = isOnLine ? eventNames().onlineEvent : eventNames().offlineEvent;
112     for (unsigned i = 0; i < frames.size(); i++)
113         frames[i]->document()->dispatchWindowEvent(Event::create(eventName, false, false));
114 }
115
116 float deviceScaleFactor(Frame* frame)
117 {
118     if (!frame)
119         return 1;
120     Page* page = frame->page();
121     if (!page)
122         return 1;
123     return page->deviceScaleFactor();
124 }
125
126 Page::Page(PageClients& pageClients)
127     : m_chrome(Chrome::create(this, pageClients.chromeClient))
128     , m_dragCaretController(DragCaretController::create())
129 #if ENABLE(DRAG_SUPPORT)
130     , m_dragController(DragController::create(this, pageClients.dragClient))
131 #endif
132     , m_focusController(FocusController::create(this))
133 #if ENABLE(CONTEXT_MENUS)
134     , m_contextMenuController(ContextMenuController::create(this, pageClients.contextMenuClient))
135 #endif
136 #if ENABLE(INSPECTOR)
137     , m_inspectorController(InspectorController::create(this, pageClients.inspectorClient))
138 #endif
139 #if ENABLE(POINTER_LOCK)
140     , m_pointerLockController(PointerLockController::create(this))
141 #endif
142     , m_settings(Settings::create(this))
143     , m_progress(ProgressTracker::create())
144     , m_backForwardController(BackForwardController::create(this, pageClients.backForwardClient))
145     , m_mainFrame(Frame::create(this, 0, pageClients.loaderClientForMainFrame))
146     , m_theme(RenderTheme::themeForPage(this))
147     , m_editorClient(pageClients.editorClient)
148     , m_plugInClient(pageClients.plugInClient)
149     , m_validationMessageClient(pageClients.validationMessageClient)
150     , m_subframeCount(0)
151     , m_openedByDOM(false)
152     , m_tabKeyCyclesThroughElements(true)
153     , m_defersLoading(false)
154     , m_defersLoadingCallCount(0)
155     , m_inLowQualityInterpolationMode(false)
156     , m_cookieEnabled(true)
157     , m_areMemoryCacheClientCallsEnabled(true)
158     , m_mediaVolume(1)
159     , m_pageScaleFactor(1)
160     , m_deviceScaleFactor(1)
161     , m_suppressScrollbarAnimations(false)
162     , m_didLoadUserStyleSheet(false)
163     , m_userStyleSheetModificationTime(0)
164     , m_group(0)
165     , m_debugger(0)
166     , m_customHTMLTokenizerTimeDelay(-1)
167     , m_customHTMLTokenizerChunkSize(-1)
168     , m_canStartMedia(true)
169 #if ENABLE(VIEW_MODE_CSS_MEDIA)
170     , m_viewMode(ViewModeWindowed)
171 #endif // ENABLE(VIEW_MODE_CSS_MEDIA)
172     , m_minimumTimerInterval(Settings::defaultMinDOMTimerInterval())
173     , m_timerAlignmentInterval(Settings::defaultDOMTimerAlignmentInterval())
174     , m_isEditable(false)
175     , m_isOnscreen(true)
176     , m_isInWindow(true)
177 #if ENABLE(PAGE_VISIBILITY_API)
178     , m_visibilityState(PageVisibilityStateVisible)
179 #endif
180     , m_requestedLayoutMilestones(0)
181     , m_headerHeight(0)
182     , m_footerHeight(0)
183     , m_isCountingRelevantRepaintedObjects(false)
184 #ifndef NDEBUG
185     , m_isPainting(false)
186 #endif
187     , m_alternativeTextClient(pageClients.alternativeTextClient)
188     , m_scriptedAnimationsSuspended(false)
189     , m_pageThrottler(PageThrottler::create(this))
190     , m_console(PageConsole::create(this))
191     , m_lastSpatialNavigationCandidatesCount(0) // NOTE: Only called from Internals for Spatial Navigation testing.
192     , m_framesHandlingBeforeUnloadEvent(0)
193 {
194     ASSERT(m_editorClient);
195
196     if (!allPages) {
197         allPages = new HashSet<Page*>;
198         
199         networkStateNotifier().addNetworkStateChangeListener(networkStateChanged);
200     }
201
202     ASSERT(!allPages->contains(this));
203     allPages->add(this);
204
205 #ifndef NDEBUG
206     pageCounter.increment();
207 #endif
208 }
209
210 Page::~Page()
211 {
212     m_mainFrame->setView(0);
213     setGroupName(String());
214     allPages->remove(this);
215     
216     for (Frame* frame = mainFrame(); frame; frame = frame->tree().traverseNext()) {
217         frame->willDetachPage();
218         frame->detachFromPage();
219     }
220
221     m_editorClient->pageDestroyed();
222     if (m_plugInClient)
223         m_plugInClient->pageDestroyed();
224     if (m_alternativeTextClient)
225         m_alternativeTextClient->pageDestroyed();
226
227 #if ENABLE(INSPECTOR)
228     m_inspectorController->inspectedPageDestroyed();
229 #endif
230
231     if (m_scrollingCoordinator)
232         m_scrollingCoordinator->pageDestroyed();
233
234     backForward()->close();
235
236 #ifndef NDEBUG
237     pageCounter.decrement();
238 #endif
239
240     m_pageThrottler.clear();
241 }
242
243 ArenaSize Page::renderTreeSize() const
244 {
245     ArenaSize total(0, 0);
246     for (Frame* frame = mainFrame(); frame; frame = frame->tree().traverseNext()) {
247         if (!frame->document())
248             continue;
249         if (RenderArena* arena = frame->document()->renderArena()) {
250             total.treeSize += arena->totalRenderArenaSize();
251             total.allocated += arena->totalRenderArenaAllocatedBytes();
252         }
253     }
254     return total;
255 }
256
257 ViewportArguments Page::viewportArguments() const
258 {
259     return mainFrame() && mainFrame()->document() ? mainFrame()->document()->viewportArguments() : ViewportArguments();
260 }
261
262 ScrollingCoordinator* Page::scrollingCoordinator()
263 {
264     if (!m_scrollingCoordinator && m_settings->scrollingCoordinatorEnabled())
265         m_scrollingCoordinator = ScrollingCoordinator::create(this);
266
267     return m_scrollingCoordinator.get();
268 }
269
270 String Page::scrollingStateTreeAsText()
271 {
272     if (Document* document = m_mainFrame->document())
273         document->updateLayout();
274
275     if (ScrollingCoordinator* scrollingCoordinator = this->scrollingCoordinator())
276         return scrollingCoordinator->scrollingStateTreeAsText();
277
278     return String();
279 }
280
281 String Page::mainThreadScrollingReasonsAsText()
282 {
283     if (Document* document = m_mainFrame->document())
284         document->updateLayout();
285
286     if (ScrollingCoordinator* scrollingCoordinator = this->scrollingCoordinator())
287         return scrollingCoordinator->mainThreadScrollingReasonsAsText();
288
289     return String();
290 }
291
292 PassRefPtr<ClientRectList> Page::nonFastScrollableRects(const Frame* frame)
293 {
294     if (Document* document = m_mainFrame->document())
295         document->updateLayout();
296
297     Vector<IntRect> rects;
298     if (ScrollingCoordinator* scrollingCoordinator = this->scrollingCoordinator())
299         rects = scrollingCoordinator->computeNonFastScrollableRegion(frame, IntPoint()).rects();
300
301     Vector<FloatQuad> quads(rects.size());
302     for (size_t i = 0; i < rects.size(); ++i)
303         quads[i] = FloatRect(rects[i]);
304     return ClientRectList::create(quads);
305 }
306
307 #if ENABLE(VIEW_MODE_CSS_MEDIA)
308 struct ViewModeInfo {
309     const char* name;
310     Page::ViewMode type;
311 };
312 static const int viewModeMapSize = 5;
313 static ViewModeInfo viewModeMap[viewModeMapSize] = {
314     {"windowed", Page::ViewModeWindowed},
315     {"floating", Page::ViewModeFloating},
316     {"fullscreen", Page::ViewModeFullscreen},
317     {"maximized", Page::ViewModeMaximized},
318     {"minimized", Page::ViewModeMinimized}
319 };
320
321 Page::ViewMode Page::stringToViewMode(const String& text)
322 {
323     for (int i = 0; i < viewModeMapSize; ++i) {
324         if (text == viewModeMap[i].name)
325             return viewModeMap[i].type;
326     }
327     return Page::ViewModeInvalid;
328 }
329
330 void Page::setViewMode(ViewMode viewMode)
331 {
332     if (viewMode == m_viewMode || viewMode == ViewModeInvalid)
333         return;
334
335     m_viewMode = viewMode;
336
337     if (!m_mainFrame)
338         return;
339
340     if (m_mainFrame->view())
341         m_mainFrame->view()->forceLayout();
342
343     if (m_mainFrame->document())
344         m_mainFrame->document()->styleResolverChanged(RecalcStyleImmediately);
345 }
346 #endif // ENABLE(VIEW_MODE_CSS_MEDIA)
347
348 bool Page::openedByDOM() const
349 {
350     return m_openedByDOM;
351 }
352
353 void Page::setOpenedByDOM()
354 {
355     m_openedByDOM = true;
356 }
357
358 BackForwardList* Page::backForwardList() const
359 {
360     return m_backForwardController->client();
361 }
362
363 bool Page::goBack()
364 {
365     HistoryItem* item = backForward()->backItem();
366     
367     if (item) {
368         goToItem(item, FrameLoadTypeBack);
369         return true;
370     }
371     return false;
372 }
373
374 bool Page::goForward()
375 {
376     HistoryItem* item = backForward()->forwardItem();
377     
378     if (item) {
379         goToItem(item, FrameLoadTypeForward);
380         return true;
381     }
382     return false;
383 }
384
385 bool Page::canGoBackOrForward(int distance) const
386 {
387     if (distance == 0)
388         return true;
389     if (distance > 0 && distance <= backForward()->forwardCount())
390         return true;
391     if (distance < 0 && -distance <= backForward()->backCount())
392         return true;
393     return false;
394 }
395
396 void Page::goBackOrForward(int distance)
397 {
398     if (distance == 0)
399         return;
400
401     HistoryItem* item = backForward()->itemAtIndex(distance);
402     if (!item) {
403         if (distance > 0) {
404             if (int forwardCount = backForward()->forwardCount()) 
405                 item = backForward()->itemAtIndex(forwardCount);
406         } else {
407             if (int backCount = backForward()->backCount())
408                 item = backForward()->itemAtIndex(-backCount);
409         }
410     }
411
412     if (!item)
413         return;
414
415     goToItem(item, FrameLoadTypeIndexedBackForward);
416 }
417
418 void Page::goToItem(HistoryItem* item, FrameLoadType type)
419 {
420     // stopAllLoaders may end up running onload handlers, which could cause further history traversals that may lead to the passed in HistoryItem
421     // being deref()-ed. Make sure we can still use it with HistoryController::goToItem later.
422     RefPtr<HistoryItem> protector(item);
423
424     if (m_mainFrame->loader().history().shouldStopLoadingForHistoryItem(item))
425         m_mainFrame->loader().stopAllLoaders();
426
427     m_mainFrame->loader().history().goToItem(item, type);
428 }
429
430 int Page::getHistoryLength()
431 {
432     return backForward()->backCount() + 1 + backForward()->forwardCount();
433 }
434
435 void Page::setGroupName(const String& name)
436 {
437     if (m_group && !m_group->name().isEmpty()) {
438         ASSERT(m_group != m_singlePageGroup.get());
439         ASSERT(!m_singlePageGroup);
440         m_group->removePage(this);
441     }
442
443     if (name.isEmpty())
444         m_group = m_singlePageGroup.get();
445     else {
446         m_singlePageGroup.clear();
447         m_group = PageGroup::pageGroup(name);
448         m_group->addPage(this);
449     }
450 }
451
452 const String& Page::groupName() const
453 {
454     return m_group ? m_group->name() : nullAtom.string();
455 }
456
457 void Page::initGroup()
458 {
459     ASSERT(!m_singlePageGroup);
460     ASSERT(!m_group);
461     m_singlePageGroup = PageGroup::create(this);
462     m_group = m_singlePageGroup.get();
463 }
464
465 void Page::updateStyleForAllPagesAfterGlobalChangeInEnvironment()
466 {
467     if (!allPages)
468         return;
469     HashSet<Page*>::iterator end = allPages->end();
470     for (HashSet<Page*>::iterator it = allPages->begin(); it != end; ++it)
471         for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree().traverseNext()) {
472             // If a change in the global environment has occurred, we need to
473             // make sure all the properties a recomputed, therefore we invalidate
474             // the properties cache.
475             if (StyleResolver* styleResolver = frame->document()->styleResolverIfExists())
476                 styleResolver->invalidateMatchedPropertiesCache();
477             frame->document()->scheduleForcedStyleRecalc();
478         }
479 }
480
481 void Page::setNeedsRecalcStyleInAllFrames()
482 {
483     for (Frame* frame = mainFrame(); frame; frame = frame->tree().traverseNext()) {
484         if (Document* document = frame->document())
485             document->styleResolverChanged(DeferRecalcStyle);
486     }
487 }
488
489 void Page::refreshPlugins(bool reload)
490 {
491     if (!allPages)
492         return;
493
494     PluginData::refresh();
495
496     Vector<RefPtr<Frame> > framesNeedingReload;
497
498     HashSet<Page*>::iterator end = allPages->end();
499     for (HashSet<Page*>::iterator it = allPages->begin(); it != end; ++it) {
500         Page* page = *it;
501         
502         // Clear out the page's plug-in data.
503         if (page->m_pluginData)
504             page->m_pluginData = 0;
505
506         if (!reload)
507             continue;
508         
509         for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree().traverseNext()) {
510             if (frame->loader().subframeLoader()->containsPlugins())
511                 framesNeedingReload.append(frame);
512         }
513     }
514
515     for (size_t i = 0; i < framesNeedingReload.size(); ++i)
516         framesNeedingReload[i]->loader().reload();
517 }
518
519 PluginData* Page::pluginData() const
520 {
521     if (!m_pluginData)
522         m_pluginData = PluginData::create(this);
523     return m_pluginData.get();
524 }
525
526 inline MediaCanStartListener* Page::takeAnyMediaCanStartListener()
527 {
528     for (Frame* frame = mainFrame(); frame; frame = frame->tree().traverseNext()) {
529         if (MediaCanStartListener* listener = frame->document()->takeAnyMediaCanStartListener())
530             return listener;
531     }
532     return 0;
533 }
534
535 void Page::setCanStartMedia(bool canStartMedia)
536 {
537     if (m_canStartMedia == canStartMedia)
538         return;
539
540     m_canStartMedia = canStartMedia;
541
542     while (m_canStartMedia) {
543         MediaCanStartListener* listener = takeAnyMediaCanStartListener();
544         if (!listener)
545             break;
546         listener->mediaCanStart();
547     }
548 }
549
550 static Frame* incrementFrame(Frame* curr, bool forward, bool wrapFlag)
551 {
552     return forward
553         ? curr->tree().traverseNextWithWrap(wrapFlag)
554         : curr->tree().traversePreviousWithWrap(wrapFlag);
555 }
556
557 bool Page::findString(const String& target, TextCaseSensitivity caseSensitivity, FindDirection direction, bool shouldWrap)
558 {
559     return findString(target, (caseSensitivity == TextCaseInsensitive ? CaseInsensitive : 0) | (direction == FindDirectionBackward ? Backwards : 0) | (shouldWrap ? WrapAround : 0));
560 }
561
562 bool Page::findString(const String& target, FindOptions options)
563 {
564     if (target.isEmpty() || !mainFrame())
565         return false;
566
567     bool shouldWrap = options & WrapAround;
568     Frame* frame = focusController().focusedOrMainFrame();
569     Frame* startFrame = frame;
570     do {
571         if (frame->editor().findString(target, (options & ~WrapAround) | StartInSelection)) {
572             if (frame != startFrame)
573                 startFrame->selection().clear();
574             focusController().setFocusedFrame(frame);
575             return true;
576         }
577         frame = incrementFrame(frame, !(options & Backwards), shouldWrap);
578     } while (frame && frame != startFrame);
579
580     // Search contents of startFrame, on the other side of the selection that we did earlier.
581     // We cheat a bit and just research with wrap on
582     if (shouldWrap && !startFrame->selection().isNone()) {
583         bool found = startFrame->editor().findString(target, options | WrapAround | StartInSelection);
584         focusController().setFocusedFrame(frame);
585         return found;
586     }
587
588     return false;
589 }
590
591 void Page::findStringMatchingRanges(const String& target, FindOptions options, int limit, Vector<RefPtr<Range> >* matchRanges, int& indexForSelection)
592 {
593     indexForSelection = 0;
594     if (!mainFrame())
595         return;
596
597     Frame* frame = mainFrame();
598     Frame* frameWithSelection = 0;
599     do {
600         frame->editor().countMatchesForText(target, 0, options, limit ? (limit - matchRanges->size()) : 0, true, matchRanges);
601         if (frame->selection().isRange())
602             frameWithSelection = frame;
603         frame = incrementFrame(frame, true, false);
604     } while (frame);
605
606     if (matchRanges->isEmpty())
607         return;
608
609     if (frameWithSelection) {
610         indexForSelection = NoMatchAfterUserSelection;
611         RefPtr<Range> selectedRange = frameWithSelection->selection().selection().firstRange();
612         if (options & Backwards) {
613             for (size_t i = matchRanges->size(); i > 0; --i) {
614                 if (selectedRange->compareBoundaryPoints(Range::END_TO_START, matchRanges->at(i - 1).get(), IGNORE_EXCEPTION) > 0) {
615                     indexForSelection = i - 1;
616                     break;
617                 }
618             }
619         } else {
620             for (size_t i = 0; i < matchRanges->size(); ++i) {
621                 if (selectedRange->compareBoundaryPoints(Range::START_TO_END, matchRanges->at(i).get(), IGNORE_EXCEPTION) < 0) {
622                     indexForSelection = i;
623                     break;
624                 }
625             }
626         }
627     } else {
628         if (options & Backwards)
629             indexForSelection = matchRanges->size() - 1;
630         else
631             indexForSelection = 0;
632     }
633 }
634
635 PassRefPtr<Range> Page::rangeOfString(const String& target, Range* referenceRange, FindOptions options)
636 {
637     if (target.isEmpty() || !mainFrame())
638         return 0;
639
640     if (referenceRange && referenceRange->ownerDocument()->page() != this)
641         return 0;
642
643     bool shouldWrap = options & WrapAround;
644     Frame* frame = referenceRange ? referenceRange->ownerDocument()->frame() : mainFrame();
645     Frame* startFrame = frame;
646     do {
647         if (RefPtr<Range> resultRange = frame->editor().rangeOfString(target, frame == startFrame ? referenceRange : 0, options & ~WrapAround))
648             return resultRange.release();
649
650         frame = incrementFrame(frame, !(options & Backwards), shouldWrap);
651     } while (frame && frame != startFrame);
652
653     // Search contents of startFrame, on the other side of the reference range that we did earlier.
654     // We cheat a bit and just search again with wrap on.
655     if (shouldWrap && referenceRange) {
656         if (RefPtr<Range> resultRange = startFrame->editor().rangeOfString(target, referenceRange, options | WrapAround | StartInSelection))
657             return resultRange.release();
658     }
659
660     return 0;
661 }
662
663 unsigned Page::findMatchesForText(const String& target, FindOptions options, unsigned maxMatchCount, ShouldHighlightMatches shouldHighlightMatches, ShouldMarkMatches shouldMarkMatches)
664 {
665     if (target.isEmpty() || !mainFrame())
666         return 0;
667
668     unsigned matchCount = 0;
669
670     Frame* frame = mainFrame();
671     do {
672         if (shouldMarkMatches == MarkMatches)
673             frame->editor().setMarkedTextMatchesAreHighlighted(shouldHighlightMatches == HighlightMatches);
674         matchCount += frame->editor().countMatchesForText(target, 0, options, maxMatchCount ? (maxMatchCount - matchCount) : 0, shouldMarkMatches == MarkMatches, 0);
675         frame = incrementFrame(frame, true, false);
676     } while (frame);
677
678     return matchCount;
679 }
680
681 unsigned Page::markAllMatchesForText(const String& target, FindOptions options, bool shouldHighlight, unsigned maxMatchCount)
682 {
683     return findMatchesForText(target, options, maxMatchCount, shouldHighlight ? HighlightMatches : DoNotHighlightMatches, MarkMatches);
684 }
685
686 unsigned Page::countFindMatches(const String& target, FindOptions options, unsigned maxMatchCount)
687 {
688     return findMatchesForText(target, options, maxMatchCount, DoNotHighlightMatches, DoNotMarkMatches);
689 }
690
691 void Page::unmarkAllTextMatches()
692 {
693     if (!mainFrame())
694         return;
695
696     Frame* frame = mainFrame();
697     do {
698         frame->document()->markers().removeMarkers(DocumentMarker::TextMatch);
699         frame = incrementFrame(frame, true, false);
700     } while (frame);
701 }
702
703 const VisibleSelection& Page::selection() const
704 {
705     return focusController().focusedOrMainFrame()->selection().selection();
706 }
707
708 void Page::setDefersLoading(bool defers)
709 {
710     if (!m_settings->loadDeferringEnabled())
711         return;
712
713     if (m_settings->wantsBalancedSetDefersLoadingBehavior()) {
714         ASSERT(defers || m_defersLoadingCallCount);
715         if (defers && ++m_defersLoadingCallCount > 1)
716             return;
717         if (!defers && --m_defersLoadingCallCount)
718             return;
719     } else {
720         ASSERT(!m_defersLoadingCallCount);
721         if (defers == m_defersLoading)
722             return;
723     }
724
725     m_defersLoading = defers;
726     for (Frame* frame = mainFrame(); frame; frame = frame->tree().traverseNext())
727         frame->loader().setDefersLoading(defers);
728 }
729
730 void Page::clearUndoRedoOperations()
731 {
732     m_editorClient->clearUndoRedoOperations();
733 }
734
735 bool Page::inLowQualityImageInterpolationMode() const
736 {
737     return m_inLowQualityInterpolationMode;
738 }
739
740 void Page::setInLowQualityImageInterpolationMode(bool mode)
741 {
742     m_inLowQualityInterpolationMode = mode;
743 }
744
745 void Page::setMediaVolume(float volume)
746 {
747     if (volume < 0 || volume > 1)
748         return;
749
750     if (m_mediaVolume == volume)
751         return;
752
753     m_mediaVolume = volume;
754     for (Frame* frame = mainFrame(); frame; frame = frame->tree().traverseNext()) {
755         frame->document()->mediaVolumeDidChange();
756     }
757 }
758
759 void Page::setPageScaleFactor(float scale, const IntPoint& origin)
760 {
761     Document* document = mainFrame()->document();
762     FrameView* view = document->view();
763
764     if (scale == m_pageScaleFactor) {
765         if (view && (view->scrollPosition() != origin || view->delegatesScrolling())) {
766             if (!m_settings->applyPageScaleFactorInCompositor())
767                 document->updateLayoutIgnorePendingStylesheets();
768             view->setScrollPosition(origin);
769         }
770         return;
771     }
772
773     m_pageScaleFactor = scale;
774
775     if (!m_settings->applyPageScaleFactorInCompositor()) {
776         if (document->renderer())
777             document->renderer()->setNeedsLayout(true);
778
779         document->recalcStyle(Style::Force);
780
781         // Transform change on RenderView doesn't trigger repaint on non-composited contents.
782         mainFrame()->view()->invalidateRect(IntRect(LayoutRect::infiniteRect()));
783     }
784
785 #if USE(ACCELERATED_COMPOSITING)
786     mainFrame()->deviceOrPageScaleFactorChanged();
787 #endif
788
789     if (view && view->fixedElementsLayoutRelativeToFrame())
790         view->setViewportConstrainedObjectsNeedLayout();
791
792     if (view && view->scrollPosition() != origin) {
793         if (!m_settings->applyPageScaleFactorInCompositor() && document->renderer() && document->renderer()->needsLayout() && view->didFirstLayout())
794             view->layout();
795         view->setScrollPosition(origin);
796     }
797 }
798
799
800 void Page::setDeviceScaleFactor(float scaleFactor)
801 {
802     if (m_deviceScaleFactor == scaleFactor)
803         return;
804
805     m_deviceScaleFactor = scaleFactor;
806     setNeedsRecalcStyleInAllFrames();
807
808 #if USE(ACCELERATED_COMPOSITING)
809     if (mainFrame())
810         mainFrame()->deviceOrPageScaleFactorChanged();
811
812     pageCache()->markPagesForDeviceScaleChanged(this);
813 #endif
814
815     for (Frame* frame = mainFrame(); frame; frame = frame->tree().traverseNext())
816         frame->editor().deviceScaleFactorChanged();
817
818     pageCache()->markPagesForFullStyleRecalc(this);
819 }
820
821 void Page::setShouldSuppressScrollbarAnimations(bool suppressAnimations)
822 {
823     if (suppressAnimations == m_suppressScrollbarAnimations)
824         return;
825
826     if (!suppressAnimations) {
827         // If animations are not going to be suppressed anymore, then there is nothing to do here but
828         // change the cached value.
829         m_suppressScrollbarAnimations = suppressAnimations;
830         return;
831     }
832
833     // On the other hand, if we are going to start suppressing animations, then we need to make sure we
834     // finish any current scroll animations first.
835     FrameView* view = mainFrame()->view();
836     if (!view)
837         return;
838
839     view->finishCurrentScrollAnimations();
840     
841     for (Frame* frame = mainFrame(); frame; frame = frame->tree().traverseNext()) {
842         FrameView* frameView = frame->view();
843         if (!frameView)
844             continue;
845
846         const HashSet<ScrollableArea*>* scrollableAreas = frameView->scrollableAreas();
847         if (!scrollableAreas)
848             continue;
849
850         for (HashSet<ScrollableArea*>::const_iterator it = scrollableAreas->begin(), end = scrollableAreas->end(); it != end; ++it) {
851             ScrollableArea* scrollableArea = *it;
852             ASSERT(scrollableArea->scrollbarsCanBeActive());
853
854             scrollableArea->finishCurrentScrollAnimations();
855         }
856     }
857
858     m_suppressScrollbarAnimations = suppressAnimations;
859 }
860
861 bool Page::rubberBandsAtBottom()
862 {
863     if (ScrollingCoordinator* scrollingCoordinator = this->scrollingCoordinator())
864         return scrollingCoordinator->rubberBandsAtBottom();
865
866     return false;
867 }
868
869 void Page::setRubberBandsAtBottom(bool rubberBandsAtBottom)
870 {
871     if (ScrollingCoordinator* scrollingCoordinator = this->scrollingCoordinator())
872         scrollingCoordinator->setRubberBandsAtBottom(rubberBandsAtBottom);
873 }
874
875 bool Page::rubberBandsAtTop()
876 {
877     if (ScrollingCoordinator* scrollingCoordinator = this->scrollingCoordinator())
878         return scrollingCoordinator->rubberBandsAtTop();
879
880     return false;
881 }
882
883 void Page::setRubberBandsAtTop(bool rubberBandsAtTop)
884 {
885     if (ScrollingCoordinator* scrollingCoordinator = this->scrollingCoordinator())
886         scrollingCoordinator->setRubberBandsAtTop(rubberBandsAtTop);
887 }
888
889 void Page::setPagination(const Pagination& pagination)
890 {
891     if (m_pagination == pagination)
892         return;
893
894     m_pagination = pagination;
895
896     setNeedsRecalcStyleInAllFrames();
897     pageCache()->markPagesForFullStyleRecalc(this);
898 }
899
900 unsigned Page::pageCount() const
901 {
902     if (m_pagination.mode == Pagination::Unpaginated)
903         return 0;
904
905     if (Document* document = mainFrame()->document())
906         document->updateLayoutIgnorePendingStylesheets();
907
908     RenderView* contentRenderer = mainFrame()->contentRenderer();
909     return contentRenderer ? contentRenderer->columnCount(contentRenderer->columnInfo()) : 0;
910 }
911
912 void Page::didMoveOnscreen()
913 {
914     m_isOnscreen = true;
915
916     for (Frame* frame = mainFrame(); frame; frame = frame->tree().traverseNext()) {
917         if (FrameView* frameView = frame->view())
918             frameView->didMoveOnscreen();
919     }
920     
921     resumeScriptedAnimations();
922 }
923
924 void Page::willMoveOffscreen()
925 {
926     m_isOnscreen = false;
927
928     for (Frame* frame = mainFrame(); frame; frame = frame->tree().traverseNext()) {
929         if (FrameView* frameView = frame->view())
930             frameView->willMoveOffscreen();
931     }
932     
933     suspendScriptedAnimations();
934 }
935
936 void Page::setIsInWindow(bool isInWindow)
937 {
938     if (m_isInWindow == isInWindow)
939         return;
940
941     m_isInWindow = isInWindow;
942
943     for (Frame* frame = mainFrame(); frame; frame = frame->tree().traverseNext()) {
944         if (FrameView* frameView = frame->view())
945             frameView->setIsInWindow(isInWindow);
946     }
947 }
948
949 void Page::suspendScriptedAnimations()
950 {
951     m_scriptedAnimationsSuspended = true;
952     for (Frame* frame = mainFrame(); frame; frame = frame->tree().traverseNext()) {
953         if (frame->document())
954             frame->document()->suspendScriptedAnimationControllerCallbacks();
955     }
956 }
957
958 void Page::resumeScriptedAnimations()
959 {
960     m_scriptedAnimationsSuspended = false;
961     for (Frame* frame = mainFrame(); frame; frame = frame->tree().traverseNext()) {
962         if (frame->document())
963             frame->document()->resumeScriptedAnimationControllerCallbacks();
964     }
965 }
966
967 void Page::setThrottled(bool throttled)
968 {
969     m_pageThrottler->setThrottled(throttled);
970 }
971
972 void Page::userStyleSheetLocationChanged()
973 {
974     // FIXME: Eventually we will move to a model of just being handed the sheet
975     // text instead of loading the URL ourselves.
976     KURL url = m_settings->userStyleSheetLocation();
977     
978     // Allow any local file URL scheme to be loaded.
979     if (SchemeRegistry::shouldTreatURLSchemeAsLocal(url.protocol()))
980         m_userStyleSheetPath = url.fileSystemPath();
981     else
982         m_userStyleSheetPath = String();
983
984     m_didLoadUserStyleSheet = false;
985     m_userStyleSheet = String();
986     m_userStyleSheetModificationTime = 0;
987
988     // Data URLs with base64-encoded UTF-8 style sheets are common. We can process them
989     // synchronously and avoid using a loader. 
990     if (url.protocolIsData() && url.string().startsWith("data:text/css;charset=utf-8;base64,")) {
991         m_didLoadUserStyleSheet = true;
992
993         Vector<char> styleSheetAsUTF8;
994         if (base64Decode(decodeURLEscapeSequences(url.string().substring(35)), styleSheetAsUTF8, Base64IgnoreWhitespace))
995             m_userStyleSheet = String::fromUTF8(styleSheetAsUTF8.data(), styleSheetAsUTF8.size());
996     }
997
998     for (Frame* frame = mainFrame(); frame; frame = frame->tree().traverseNext()) {
999         if (frame->document())
1000             frame->document()->styleSheetCollection()->updatePageUserSheet();
1001     }
1002 }
1003
1004 const String& Page::userStyleSheet() const
1005 {
1006     if (m_userStyleSheetPath.isEmpty())
1007         return m_userStyleSheet;
1008
1009     time_t modTime;
1010     if (!getFileModificationTime(m_userStyleSheetPath, modTime)) {
1011         // The stylesheet either doesn't exist, was just deleted, or is
1012         // otherwise unreadable. If we've read the stylesheet before, we should
1013         // throw away that data now as it no longer represents what's on disk.
1014         m_userStyleSheet = String();
1015         return m_userStyleSheet;
1016     }
1017
1018     // If the stylesheet hasn't changed since the last time we read it, we can
1019     // just return the old data.
1020     if (m_didLoadUserStyleSheet && modTime <= m_userStyleSheetModificationTime)
1021         return m_userStyleSheet;
1022
1023     m_didLoadUserStyleSheet = true;
1024     m_userStyleSheet = String();
1025     m_userStyleSheetModificationTime = modTime;
1026
1027     // FIXME: It would be better to load this asynchronously to avoid blocking
1028     // the process, but we will first need to create an asynchronous loading
1029     // mechanism that is not tied to a particular Frame. We will also have to
1030     // determine what our behavior should be before the stylesheet is loaded
1031     // and what should happen when it finishes loading, especially with respect
1032     // to when the load event fires, when Document::close is called, and when
1033     // layout/paint are allowed to happen.
1034     RefPtr<SharedBuffer> data = SharedBuffer::createWithContentsOfFile(m_userStyleSheetPath);
1035     if (!data)
1036         return m_userStyleSheet;
1037
1038     RefPtr<TextResourceDecoder> decoder = TextResourceDecoder::create("text/css");
1039     m_userStyleSheet = decoder->decode(data->data(), data->size());
1040     m_userStyleSheet.append(decoder->flush());
1041
1042     return m_userStyleSheet;
1043 }
1044
1045 void Page::removeAllVisitedLinks()
1046 {
1047     if (!allPages)
1048         return;
1049     HashSet<PageGroup*> groups;
1050     HashSet<Page*>::iterator pagesEnd = allPages->end();
1051     for (HashSet<Page*>::iterator it = allPages->begin(); it != pagesEnd; ++it) {
1052         if (PageGroup* group = (*it)->groupPtr())
1053             groups.add(group);
1054     }
1055     HashSet<PageGroup*>::iterator groupsEnd = groups.end();
1056     for (HashSet<PageGroup*>::iterator it = groups.begin(); it != groupsEnd; ++it)
1057         (*it)->removeVisitedLinks();
1058 }
1059
1060 void Page::allVisitedStateChanged(PageGroup* group)
1061 {
1062     ASSERT(group);
1063     if (!allPages)
1064         return;
1065
1066     HashSet<Page*>::iterator pagesEnd = allPages->end();
1067     for (HashSet<Page*>::iterator it = allPages->begin(); it != pagesEnd; ++it) {
1068         Page* page = *it;
1069         if (page->m_group != group)
1070             continue;
1071         for (Frame* frame = page->m_mainFrame.get(); frame; frame = frame->tree().traverseNext())
1072             frame->document()->visitedLinkState().invalidateStyleForAllLinks();
1073     }
1074 }
1075
1076 void Page::visitedStateChanged(PageGroup* group, LinkHash linkHash)
1077 {
1078     ASSERT(group);
1079     if (!allPages)
1080         return;
1081
1082     HashSet<Page*>::iterator pagesEnd = allPages->end();
1083     for (HashSet<Page*>::iterator it = allPages->begin(); it != pagesEnd; ++it) {
1084         Page* page = *it;
1085         if (page->m_group != group)
1086             continue;
1087         for (Frame* frame = page->m_mainFrame.get(); frame; frame = frame->tree().traverseNext())
1088             frame->document()->visitedLinkState().invalidateStyleForLink(linkHash);
1089     }
1090 }
1091
1092 void Page::setDebuggerForAllPages(JSC::Debugger* debugger)
1093 {
1094     ASSERT(allPages);
1095
1096     HashSet<Page*>::iterator end = allPages->end();
1097     for (HashSet<Page*>::iterator it = allPages->begin(); it != end; ++it)
1098         (*it)->setDebugger(debugger);
1099 }
1100
1101 void Page::setDebugger(JSC::Debugger* debugger)
1102 {
1103     if (m_debugger == debugger)
1104         return;
1105
1106     m_debugger = debugger;
1107
1108     for (Frame* frame = m_mainFrame.get(); frame; frame = frame->tree().traverseNext())
1109         frame->script().attachDebugger(m_debugger);
1110 }
1111
1112 StorageNamespace* Page::sessionStorage(bool optionalCreate)
1113 {
1114     if (!m_sessionStorage && optionalCreate)
1115         m_sessionStorage = StorageNamespace::sessionStorageNamespace(this);
1116
1117     return m_sessionStorage.get();
1118 }
1119
1120 void Page::setSessionStorage(PassRefPtr<StorageNamespace> newStorage)
1121 {
1122     m_sessionStorage = newStorage;
1123 }
1124
1125 void Page::setCustomHTMLTokenizerTimeDelay(double customHTMLTokenizerTimeDelay)
1126 {
1127     if (customHTMLTokenizerTimeDelay < 0) {
1128         m_customHTMLTokenizerTimeDelay = -1;
1129         return;
1130     }
1131     m_customHTMLTokenizerTimeDelay = customHTMLTokenizerTimeDelay;
1132 }
1133
1134 void Page::setCustomHTMLTokenizerChunkSize(int customHTMLTokenizerChunkSize)
1135 {
1136     if (customHTMLTokenizerChunkSize < 0) {
1137         m_customHTMLTokenizerChunkSize = -1;
1138         return;
1139     }
1140     m_customHTMLTokenizerChunkSize = customHTMLTokenizerChunkSize;
1141 }
1142
1143 void Page::setMemoryCacheClientCallsEnabled(bool enabled)
1144 {
1145     if (m_areMemoryCacheClientCallsEnabled == enabled)
1146         return;
1147
1148     m_areMemoryCacheClientCallsEnabled = enabled;
1149     if (!enabled)
1150         return;
1151
1152     for (RefPtr<Frame> frame = mainFrame(); frame; frame = frame->tree().traverseNext())
1153         frame->loader().tellClientAboutPastMemoryCacheLoads();
1154 }
1155
1156 void Page::setMinimumTimerInterval(double minimumTimerInterval)
1157 {
1158     double oldTimerInterval = m_minimumTimerInterval;
1159     m_minimumTimerInterval = minimumTimerInterval;
1160     for (Frame* frame = mainFrame(); frame; frame = frame->tree().traverseNextWithWrap(false)) {
1161         if (frame->document())
1162             frame->document()->adjustMinimumTimerInterval(oldTimerInterval);
1163     }
1164 }
1165
1166 double Page::minimumTimerInterval() const
1167 {
1168     return m_minimumTimerInterval;
1169 }
1170
1171 void Page::setTimerAlignmentInterval(double interval)
1172 {
1173     if (interval == m_timerAlignmentInterval)
1174         return;
1175
1176     m_timerAlignmentInterval = interval;
1177     for (Frame* frame = mainFrame(); frame; frame = frame->tree().traverseNextWithWrap(false)) {
1178         if (frame->document())
1179             frame->document()->didChangeTimerAlignmentInterval();
1180     }
1181 }
1182
1183 double Page::timerAlignmentInterval() const
1184 {
1185     return m_timerAlignmentInterval;
1186 }
1187
1188 void Page::dnsPrefetchingStateChanged()
1189 {
1190     for (Frame* frame = mainFrame(); frame; frame = frame->tree().traverseNext())
1191         frame->document()->initDNSPrefetch();
1192 }
1193
1194 void Page::collectPluginViews(Vector<RefPtr<PluginViewBase>, 32>& pluginViewBases)
1195 {
1196     for (Frame* frame = mainFrame(); frame; frame = frame->tree().traverseNext()) {
1197         FrameView* view = frame->view();
1198         if (!view)
1199             return;
1200
1201         const HashSet<RefPtr<Widget> >* children = view->children();
1202         ASSERT(children);
1203
1204         HashSet<RefPtr<Widget> >::const_iterator end = children->end();
1205         for (HashSet<RefPtr<Widget> >::const_iterator it = children->begin(); it != end; ++it) {
1206             Widget* widget = (*it).get();
1207             if (widget->isPluginViewBase())
1208                 pluginViewBases.append(toPluginViewBase(widget));
1209         }
1210     }
1211 }
1212
1213 void Page::storageBlockingStateChanged()
1214 {
1215     for (Frame* frame = mainFrame(); frame; frame = frame->tree().traverseNext())
1216         frame->document()->storageBlockingStateDidChange();
1217
1218     // Collect the PluginViews in to a vector to ensure that action the plug-in takes
1219     // from below storageBlockingStateChanged does not affect their lifetime.
1220     Vector<RefPtr<PluginViewBase>, 32> pluginViewBases;
1221     collectPluginViews(pluginViewBases);
1222
1223     for (size_t i = 0; i < pluginViewBases.size(); ++i)
1224         pluginViewBases[i]->storageBlockingStateChanged();
1225 }
1226
1227 void Page::privateBrowsingStateChanged()
1228 {
1229     bool privateBrowsingEnabled = m_settings->privateBrowsingEnabled();
1230
1231     for (Frame* frame = mainFrame(); frame; frame = frame->tree().traverseNext())
1232         frame->document()->privateBrowsingStateDidChange();
1233
1234     // Collect the PluginViews in to a vector to ensure that action the plug-in takes
1235     // from below privateBrowsingStateChanged does not affect their lifetime.
1236     Vector<RefPtr<PluginViewBase>, 32> pluginViewBases;
1237     collectPluginViews(pluginViewBases);
1238
1239     for (size_t i = 0; i < pluginViewBases.size(); ++i)
1240         pluginViewBases[i]->privateBrowsingStateChanged(privateBrowsingEnabled);
1241 }
1242
1243 #if !ASSERT_DISABLED
1244 void Page::checkSubframeCountConsistency() const
1245 {
1246     ASSERT(m_subframeCount >= 0);
1247
1248     int subframeCount = 0;
1249     for (Frame* frame = mainFrame(); frame; frame = frame->tree().traverseNext())
1250         ++subframeCount;
1251
1252     ASSERT(m_subframeCount + 1 == subframeCount);
1253 }
1254 #endif
1255
1256 void Page::throttleTimers()
1257 {
1258 #if ENABLE(HIDDEN_PAGE_DOM_TIMER_THROTTLING)
1259     if (m_settings->hiddenPageDOMTimerThrottlingEnabled())
1260         setTimerAlignmentInterval(Settings::hiddenPageDOMTimerAlignmentInterval());
1261 #endif
1262 }
1263
1264 void Page::unthrottleTimers()
1265 {
1266 #if ENABLE(HIDDEN_PAGE_DOM_TIMER_THROTTLING)
1267     if (m_settings->hiddenPageDOMTimerThrottlingEnabled())
1268         setTimerAlignmentInterval(Settings::defaultDOMTimerAlignmentInterval());
1269 #endif
1270 }
1271
1272 #if ENABLE(PAGE_VISIBILITY_API) || ENABLE(HIDDEN_PAGE_DOM_TIMER_THROTTLING)
1273 void Page::setVisibilityState(PageVisibilityState visibilityState, bool isInitialState)
1274 {
1275 #if !ENABLE(PAGE_VISIBILITY_API)
1276     UNUSED_PARAM(isInitialState);
1277 #else
1278     // FIXME: The visibility state should be stored on the top-level document.
1279     // https://bugs.webkit.org/show_bug.cgi?id=116769
1280
1281     if (m_visibilityState == visibilityState)
1282         return;
1283     m_visibilityState = visibilityState;
1284
1285     if (!isInitialState) {
1286         Vector<RefPtr<Document>> documents;
1287         for (Frame* frame = m_mainFrame.get(); frame; frame = frame->tree().traverseNext())
1288             documents.append(frame->document());
1289         for (size_t i = 0, size = documents.size(); i < size; ++i)
1290             documents[i]->dispatchEvent(Event::create(eventNames().visibilitychangeEvent, false, false));
1291     }
1292 #endif
1293
1294     if (visibilityState == WebCore::PageVisibilityStateHidden) {
1295         if (m_pageThrottler->shouldThrottleTimers())
1296             throttleTimers();
1297         if (m_settings->hiddenPageCSSAnimationSuspensionEnabled())
1298             mainFrame()->animation().suspendAnimations();
1299     } else {
1300         unthrottleTimers();
1301         if (m_settings->hiddenPageCSSAnimationSuspensionEnabled())
1302             mainFrame()->animation().resumeAnimations();
1303     }
1304 }
1305 #endif // ENABLE(PAGE_VISIBILITY_API) || ENABLE(HIDDEN_PAGE_DOM_TIMER_THROTTLING)
1306
1307 #if ENABLE(PAGE_VISIBILITY_API)
1308 PageVisibilityState Page::visibilityState() const
1309 {
1310     return m_visibilityState;
1311 }
1312 #endif
1313
1314 #if ENABLE(RUBBER_BANDING)
1315 void Page::addHeaderWithHeight(int headerHeight)
1316 {
1317     m_headerHeight = headerHeight;
1318
1319     FrameView* frameView = mainFrame() ? mainFrame()->view() : 0;
1320     if (!frameView)
1321         return;
1322
1323     RenderView* renderView = frameView->renderView();
1324     if (!renderView)
1325         return;
1326
1327     frameView->setHeaderHeight(m_headerHeight);
1328     renderView->compositor().updateLayerForHeader(m_headerHeight);
1329 }
1330
1331 void Page::addFooterWithHeight(int footerHeight)
1332 {
1333     m_footerHeight = footerHeight;
1334
1335     FrameView* frameView = mainFrame() ? mainFrame()->view() : 0;
1336     if (!frameView)
1337         return;
1338
1339     RenderView* renderView = frameView->renderView();
1340     if (!renderView)
1341         return;
1342
1343     frameView->setFooterHeight(m_footerHeight);
1344     renderView->compositor().updateLayerForFooter(m_footerHeight);
1345 }
1346 #endif
1347
1348 void Page::addLayoutMilestones(LayoutMilestones milestones)
1349 {
1350     // In the future, we may want a function that replaces m_layoutMilestones instead of just adding to it.
1351     m_requestedLayoutMilestones |= milestones;
1352 }
1353
1354 void Page::removeLayoutMilestones(LayoutMilestones milestones)
1355 {
1356     m_requestedLayoutMilestones &= ~milestones;
1357 }
1358
1359 // These are magical constants that might be tweaked over time.
1360 static double gMinimumPaintedAreaRatio = 0.1;
1361 static double gMaximumUnpaintedAreaRatio = 0.04;
1362
1363 bool Page::isCountingRelevantRepaintedObjects() const
1364 {
1365     return m_isCountingRelevantRepaintedObjects && (m_requestedLayoutMilestones & DidHitRelevantRepaintedObjectsAreaThreshold);
1366 }
1367
1368 void Page::startCountingRelevantRepaintedObjects()
1369 {
1370     // Reset everything in case we didn't hit the threshold last time.
1371     resetRelevantPaintedObjectCounter();
1372
1373     m_isCountingRelevantRepaintedObjects = true;
1374 }
1375
1376 void Page::resetRelevantPaintedObjectCounter()
1377 {
1378     m_isCountingRelevantRepaintedObjects = false;
1379     m_relevantUnpaintedRenderObjects.clear();
1380     m_topRelevantPaintedRegion = Region();
1381     m_bottomRelevantPaintedRegion = Region();
1382     m_relevantUnpaintedRegion = Region();
1383 }
1384
1385 static LayoutRect relevantViewRect(RenderView* view)
1386 {
1387     // DidHitRelevantRepaintedObjectsAreaThreshold is a LayoutMilestone intended to indicate that
1388     // a certain relevant amount of content has been drawn to the screen. This is the rect that
1389     // has been determined to be relevant in the context of this goal. We may choose to tweak
1390     // the rect over time, much like we may choose to tweak gMinimumPaintedAreaRatio and
1391     // gMaximumUnpaintedAreaRatio. But this seems to work well right now.
1392     LayoutRect relevantViewRect = LayoutRect(0, 0, 980, 1300);
1393
1394     LayoutRect viewRect = view->viewRect();
1395     // If the viewRect is wider than the relevantViewRect, center the relevantViewRect.
1396     if (viewRect.width() > relevantViewRect.width())
1397         relevantViewRect.setX((viewRect.width() - relevantViewRect.width()) / 2);
1398
1399     return relevantViewRect;
1400 }
1401
1402 void Page::addRelevantRepaintedObject(RenderObject* object, const LayoutRect& objectPaintRect)
1403 {
1404     if (!isCountingRelevantRepaintedObjects())
1405         return;
1406
1407     // Objects inside sub-frames are not considered to be relevant.
1408     if (&object->frame() != mainFrame())
1409         return;
1410
1411     LayoutRect relevantRect = relevantViewRect(&object->view());
1412
1413     // The objects are only relevant if they are being painted within the viewRect().
1414     if (!objectPaintRect.intersects(pixelSnappedIntRect(relevantRect)))
1415         return;
1416
1417     IntRect snappedPaintRect = pixelSnappedIntRect(objectPaintRect);
1418
1419     // If this object was previously counted as an unpainted object, remove it from that HashSet
1420     // and corresponding Region. FIXME: This doesn't do the right thing if the objects overlap.
1421     HashSet<RenderObject*>::iterator it = m_relevantUnpaintedRenderObjects.find(object);
1422     if (it != m_relevantUnpaintedRenderObjects.end()) {
1423         m_relevantUnpaintedRenderObjects.remove(it);
1424         m_relevantUnpaintedRegion.subtract(snappedPaintRect);
1425     }
1426
1427     // Split the relevantRect into a top half and a bottom half. Making sure we have coverage in
1428     // both halves helps to prevent cases where we have a fully loaded menu bar or masthead with
1429     // no content beneath that.
1430     LayoutRect topRelevantRect = relevantRect;
1431     topRelevantRect.contract(LayoutSize(0, relevantRect.height() / 2));
1432     LayoutRect bottomRelevantRect = topRelevantRect;
1433     bottomRelevantRect.setY(relevantRect.height() / 2);
1434
1435     // If the rect straddles both Regions, split it appropriately.
1436     if (topRelevantRect.intersects(snappedPaintRect) && bottomRelevantRect.intersects(snappedPaintRect)) {
1437         IntRect topIntersection = snappedPaintRect;
1438         topIntersection.intersect(pixelSnappedIntRect(topRelevantRect));
1439         m_topRelevantPaintedRegion.unite(topIntersection);
1440
1441         IntRect bottomIntersection = snappedPaintRect;
1442         bottomIntersection.intersect(pixelSnappedIntRect(bottomRelevantRect));
1443         m_bottomRelevantPaintedRegion.unite(bottomIntersection);
1444     } else if (topRelevantRect.intersects(snappedPaintRect))
1445         m_topRelevantPaintedRegion.unite(snappedPaintRect);
1446     else
1447         m_bottomRelevantPaintedRegion.unite(snappedPaintRect);
1448
1449     float topPaintedArea = m_topRelevantPaintedRegion.totalArea();
1450     float bottomPaintedArea = m_bottomRelevantPaintedRegion.totalArea();
1451     float viewArea = relevantRect.width() * relevantRect.height();
1452
1453     float ratioThatIsPaintedOnTop = topPaintedArea / viewArea;
1454     float ratioThatIsPaintedOnBottom = bottomPaintedArea / viewArea;
1455     float ratioOfViewThatIsUnpainted = m_relevantUnpaintedRegion.totalArea() / viewArea;
1456
1457     if (ratioThatIsPaintedOnTop > (gMinimumPaintedAreaRatio / 2) && ratioThatIsPaintedOnBottom > (gMinimumPaintedAreaRatio / 2)
1458         && ratioOfViewThatIsUnpainted < gMaximumUnpaintedAreaRatio) {
1459         m_isCountingRelevantRepaintedObjects = false;
1460         resetRelevantPaintedObjectCounter();
1461         if (Frame* frame = mainFrame())
1462             frame->loader().didLayout(DidHitRelevantRepaintedObjectsAreaThreshold);
1463     }
1464 }
1465
1466 void Page::addRelevantUnpaintedObject(RenderObject* object, const LayoutRect& objectPaintRect)
1467 {
1468     if (!isCountingRelevantRepaintedObjects())
1469         return;
1470
1471     // The objects are only relevant if they are being painted within the relevantViewRect().
1472     if (!objectPaintRect.intersects(pixelSnappedIntRect(relevantViewRect(&object->view()))))
1473         return;
1474
1475     m_relevantUnpaintedRenderObjects.add(object);
1476     m_relevantUnpaintedRegion.unite(pixelSnappedIntRect(objectPaintRect));
1477 }
1478
1479 void Page::suspendActiveDOMObjectsAndAnimations()
1480 {
1481     for (Frame* frame = mainFrame(); frame; frame = frame->tree().traverseNext())
1482         frame->suspendActiveDOMObjectsAndAnimations();
1483 }
1484
1485 void Page::resumeActiveDOMObjectsAndAnimations()
1486 {
1487     for (Frame* frame = mainFrame(); frame; frame = frame->tree().traverseNext())
1488         frame->resumeActiveDOMObjectsAndAnimations();
1489 }
1490
1491 bool Page::hasSeenAnyPlugin() const
1492 {
1493     return !m_seenPlugins.isEmpty();
1494 }
1495
1496 bool Page::hasSeenPlugin(const String& serviceType) const
1497 {
1498     return m_seenPlugins.contains(serviceType);
1499 }
1500
1501 void Page::sawPlugin(const String& serviceType)
1502 {
1503     m_seenPlugins.add(serviceType);
1504 }
1505
1506 void Page::resetSeenPlugins()
1507 {
1508     m_seenPlugins.clear();
1509 }
1510
1511 bool Page::hasSeenAnyMediaEngine() const
1512 {
1513     return !m_seenMediaEngines.isEmpty();
1514 }
1515
1516 bool Page::hasSeenMediaEngine(const String& engineDescription) const
1517 {
1518     return m_seenMediaEngines.contains(engineDescription);
1519 }
1520
1521 void Page::sawMediaEngine(const String& engineDescription)
1522 {
1523     m_seenMediaEngines.add(engineDescription);
1524 }
1525
1526 void Page::resetSeenMediaEngines()
1527 {
1528     m_seenMediaEngines.clear();
1529 }
1530
1531 PassOwnPtr<PageActivityAssertionToken> Page::createActivityToken()
1532 {
1533     return adoptPtr(new PageActivityAssertionToken(m_pageThrottler.get()));
1534 }
1535
1536 #if ENABLE(HIDDEN_PAGE_DOM_TIMER_THROTTLING)
1537 void Page::hiddenPageDOMTimerThrottlingStateChanged()
1538 {
1539     if (m_settings->hiddenPageDOMTimerThrottlingEnabled()) {
1540 #if ENABLE(PAGE_VISIBILITY_API)
1541         if (m_pageThrottler->shouldThrottleTimers())
1542             setTimerAlignmentInterval(Settings::hiddenPageDOMTimerAlignmentInterval());
1543 #endif
1544     } else
1545         setTimerAlignmentInterval(Settings::defaultDOMTimerAlignmentInterval());
1546 }
1547 #endif
1548
1549 #if (ENABLE_PAGE_VISIBILITY_API)
1550 void Page::hiddenPageCSSAnimationSuspensionStateChanged()
1551 {
1552     if (m_visibilityState == WebCore::PageVisibilityStateHidden) {
1553         if (m_settings->hiddenPageCSSAnimationSuspensionEnabled())
1554             mainFrame()->animation().suspendAnimations();
1555         else
1556             mainFrame()->animation().resumeAnimations();
1557     }
1558 }
1559 #endif
1560
1561 #if ENABLE(VIDEO_TRACK)
1562 void Page::captionPreferencesChanged()
1563 {
1564     for (Frame* frame = mainFrame(); frame; frame = frame->tree().traverseNext())
1565         frame->document()->captionPreferencesChanged();
1566 }
1567 #endif
1568
1569 void Page::incrementFrameHandlingBeforeUnloadEventCount()
1570 {
1571     ++m_framesHandlingBeforeUnloadEvent;
1572 }
1573
1574 void Page::decrementFrameHandlingBeforeUnloadEventCount()
1575 {
1576     ASSERT(m_framesHandlingBeforeUnloadEvent);
1577     --m_framesHandlingBeforeUnloadEvent;
1578 }
1579
1580 bool Page::isAnyFrameHandlingBeforeUnloadEvent()
1581 {
1582     return m_framesHandlingBeforeUnloadEvent;
1583 }
1584
1585 Page::PageClients::PageClients()
1586     : alternativeTextClient(0)
1587     , chromeClient(0)
1588 #if ENABLE(CONTEXT_MENUS)
1589     , contextMenuClient(0)
1590 #endif
1591     , editorClient(0)
1592     , dragClient(0)
1593     , inspectorClient(0)
1594     , plugInClient(0)
1595     , validationMessageClient(0)
1596     , loaderClientForMainFrame(0)
1597 {
1598 }
1599
1600 Page::PageClients::~PageClients()
1601 {
1602 }
1603
1604 } // namespace WebCore