• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006, 2007, 2008 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  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this library; see the file COPYING.LIB.  If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 #include "config.h"
22 #include "Page.h"
23 
24 #include "Chrome.h"
25 #include "ChromeClient.h"
26 #include "ContextMenuClient.h"
27 #include "ContextMenuController.h"
28 #include "CSSStyleSelector.h"
29 #include "EditorClient.h"
30 #include "DOMWindow.h"
31 #include "DragController.h"
32 #include "EventNames.h"
33 #include "FileSystem.h"
34 #include "FocusController.h"
35 #include "Frame.h"
36 #include "FrameLoader.h"
37 #include "FrameTree.h"
38 #include "FrameView.h"
39 #include "HistoryItem.h"
40 #include "InspectorController.h"
41 #include "Logging.h"
42 #include "NetworkStateNotifier.h"
43 #include "Navigator.h"
44 #include "PageGroup.h"
45 #include "PluginData.h"
46 #include "ProgressTracker.h"
47 #include "RenderWidget.h"
48 #include "SelectionController.h"
49 #include "Settings.h"
50 #include "StringHash.h"
51 #include "TextResourceDecoder.h"
52 #include "Widget.h"
53 #include "ScriptController.h"
54 #include <wtf/HashMap.h>
55 #include <wtf/RefCountedLeakCounter.h>
56 #include <wtf/StdLibExtras.h>
57 
58 #if ENABLE(DOM_STORAGE)
59 #include "LocalStorage.h"
60 #include "SessionStorage.h"
61 #include "StorageArea.h"
62 #endif
63 
64 #if ENABLE(JAVASCRIPT_DEBUGGER)
65 #include "JavaScriptDebugServer.h"
66 #endif
67 
68 #if ENABLE(WML)
69 #include "WMLPageState.h"
70 #endif
71 
72 namespace WebCore {
73 
74 static HashSet<Page*>* allPages;
75 
76 #ifndef NDEBUG
77 static WTF::RefCountedLeakCounter pageCounter("Page");
78 #endif
79 
networkStateChanged()80 static void networkStateChanged()
81 {
82     Vector<RefPtr<Frame> > frames;
83 
84     // Get all the frames of all the pages in all the page groups
85     HashSet<Page*>::iterator end = allPages->end();
86     for (HashSet<Page*>::iterator it = allPages->begin(); it != end; ++it) {
87         for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree()->traverseNext())
88             frames.append(frame);
89     }
90 
91     AtomicString eventName = networkStateNotifier().onLine() ? eventNames().onlineEvent : eventNames().offlineEvent;
92 
93     for (unsigned i = 0; i < frames.size(); i++) {
94         Document* document = frames[i]->document();
95 
96         if (!document)
97             continue;
98 
99         // If the document does not have a body the event should be dispatched to the document
100         EventTargetNode* eventTarget = document->body();
101         if (!eventTarget)
102             eventTarget = document;
103 
104         eventTarget->dispatchEventForType(eventName, false, false);
105     }
106 }
107 
Page(ChromeClient * chromeClient,ContextMenuClient * contextMenuClient,EditorClient * editorClient,DragClient * dragClient,InspectorClient * inspectorClient)108 Page::Page(ChromeClient* chromeClient, ContextMenuClient* contextMenuClient, EditorClient* editorClient, DragClient* dragClient, InspectorClient* inspectorClient)
109     : m_chrome(new Chrome(this, chromeClient))
110     , m_dragCaretController(new SelectionController(0, true))
111     , m_dragController(new DragController(this, dragClient))
112     , m_focusController(new FocusController(this))
113     , m_contextMenuController(new ContextMenuController(this, contextMenuClient))
114     , m_inspectorController(new InspectorController(this, inspectorClient))
115     , m_settings(new Settings(this))
116     , m_progress(new ProgressTracker)
117     , m_backForwardList(BackForwardList::create(this))
118     , m_editorClient(editorClient)
119     , m_frameCount(0)
120     , m_tabKeyCyclesThroughElements(true)
121     , m_defersLoading(false)
122     , m_inLowQualityInterpolationMode(false)
123     , m_cookieEnabled(true)
124     , m_areMemoryCacheClientCallsEnabled(true)
125     , m_mediaVolume(1)
126     , m_parentInspectorController(0)
127     , m_didLoadUserStyleSheet(false)
128     , m_userStyleSheetModificationTime(0)
129     , m_group(0)
130     , m_debugger(0)
131     , m_pendingUnloadEventCount(0)
132     , m_pendingBeforeUnloadEventCount(0)
133     , m_customHTMLTokenizerTimeDelay(-1)
134     , m_customHTMLTokenizerChunkSize(-1)
135 {
136     if (!allPages) {
137         allPages = new HashSet<Page*>;
138 
139         networkStateNotifier().setNetworkStateChangedFunction(networkStateChanged);
140     }
141 
142     ASSERT(!allPages->contains(this));
143     allPages->add(this);
144 
145 #if ENABLE(JAVASCRIPT_DEBUGGER)
146     JavaScriptDebugServer::shared().pageCreated(this);
147 #endif
148 
149 #ifndef NDEBUG
150     pageCounter.increment();
151 #endif
152 }
153 
~Page()154 Page::~Page()
155 {
156     m_mainFrame->setView(0);
157     setGroupName(String());
158     allPages->remove(this);
159 
160     for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext()) {
161         if (frame->document())
162             frame->document()->documentWillBecomeInactive();
163         frame->pageDestroyed();
164     }
165     m_editorClient->pageDestroyed();
166     if (m_parentInspectorController)
167         m_parentInspectorController->pageDestroyed();
168     m_inspectorController->inspectedPageDestroyed();
169 
170     m_backForwardList->close();
171 
172 #ifndef NDEBUG
173     pageCounter.decrement();
174 
175     // Cancel keepAlive timers, to ensure we release all Frames before exiting.
176     // It's safe to do this because we prohibit closing a Page while JavaScript
177     // is executing.
178     Frame::cancelAllKeepAlive();
179 #endif
180 }
181 
setMainFrame(PassRefPtr<Frame> mainFrame)182 void Page::setMainFrame(PassRefPtr<Frame> mainFrame)
183 {
184     ASSERT(!m_mainFrame); // Should only be called during initialization
185     m_mainFrame = mainFrame;
186 }
187 
backForwardList()188 BackForwardList* Page::backForwardList()
189 {
190     return m_backForwardList.get();
191 }
192 
goBack()193 bool Page::goBack()
194 {
195     HistoryItem* item = m_backForwardList->backItem();
196 
197     if (item) {
198         goToItem(item, FrameLoadTypeBack);
199         return true;
200     }
201     return false;
202 }
203 
goForward()204 bool Page::goForward()
205 {
206     HistoryItem* item = m_backForwardList->forwardItem();
207 
208     if (item) {
209         goToItem(item, FrameLoadTypeForward);
210         return true;
211     }
212     return false;
213 }
214 
goToItem(HistoryItem * item,FrameLoadType type)215 void Page::goToItem(HistoryItem* item, FrameLoadType type)
216 {
217     // Abort any current load if we're going to a history item
218     m_mainFrame->loader()->stopAllLoaders();
219     m_mainFrame->loader()->goToItem(item, type);
220 }
221 
setGlobalHistoryItem(HistoryItem * item)222 void Page::setGlobalHistoryItem(HistoryItem* item)
223 {
224     m_globalHistoryItem = item;
225 }
226 
setGroupName(const String & name)227 void Page::setGroupName(const String& name)
228 {
229     if (m_group && !m_group->name().isEmpty()) {
230         ASSERT(m_group != m_singlePageGroup.get());
231         ASSERT(!m_singlePageGroup);
232         m_group->removePage(this);
233     }
234 
235     if (name.isEmpty())
236         m_group = 0;
237     else {
238         m_singlePageGroup.clear();
239         m_group = PageGroup::pageGroup(name);
240         m_group->addPage(this);
241     }
242 }
243 
groupName() const244 const String& Page::groupName() const
245 {
246     DEFINE_STATIC_LOCAL(String, nullString, ());
247     return m_group ? m_group->name() : nullString;
248 }
249 
initGroup()250 void Page::initGroup()
251 {
252     ASSERT(!m_singlePageGroup);
253     ASSERT(!m_group);
254     m_singlePageGroup.set(new PageGroup(this));
255     m_group = m_singlePageGroup.get();
256 }
257 
setNeedsReapplyStyles()258 void Page::setNeedsReapplyStyles()
259 {
260     if (!allPages)
261         return;
262     HashSet<Page*>::iterator end = allPages->end();
263     for (HashSet<Page*>::iterator it = allPages->begin(); it != end; ++it)
264         for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree()->traverseNext())
265             frame->setNeedsReapplyStyles();
266 }
267 
refreshPlugins(bool reload)268 void Page::refreshPlugins(bool reload)
269 {
270     if (!allPages)
271         return;
272 
273     PluginData::refresh();
274 
275     Vector<RefPtr<Frame> > framesNeedingReload;
276 
277     HashSet<Page*>::iterator end = allPages->end();
278     for (HashSet<Page*>::iterator it = allPages->begin(); it != end; ++it) {
279         (*it)->m_pluginData = 0;
280 
281         if (reload) {
282             for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
283                 if (frame->loader()->containsPlugins())
284                     framesNeedingReload.append(frame);
285             }
286         }
287     }
288 
289     for (size_t i = 0; i < framesNeedingReload.size(); ++i)
290         framesNeedingReload[i]->loader()->reload();
291 }
292 
pluginData() const293 PluginData* Page::pluginData() const
294 {
295     if (!settings()->arePluginsEnabled())
296         return 0;
297     if (!m_pluginData)
298         m_pluginData = PluginData::create(this);
299     return m_pluginData.get();
300 }
301 
incrementFrame(Frame * curr,bool forward,bool wrapFlag)302 static Frame* incrementFrame(Frame* curr, bool forward, bool wrapFlag)
303 {
304     return forward
305         ? curr->tree()->traverseNextWithWrap(wrapFlag)
306         : curr->tree()->traversePreviousWithWrap(wrapFlag);
307 }
308 
findString(const String & target,TextCaseSensitivity caseSensitivity,FindDirection direction,bool shouldWrap)309 bool Page::findString(const String& target, TextCaseSensitivity caseSensitivity, FindDirection direction, bool shouldWrap)
310 {
311     if (target.isEmpty() || !mainFrame())
312         return false;
313 
314     Frame* frame = focusController()->focusedOrMainFrame();
315     Frame* startFrame = frame;
316     do {
317         if (frame->findString(target, direction == FindDirectionForward, caseSensitivity == TextCaseSensitive, false, true)) {
318             if (frame != startFrame)
319                 startFrame->selection()->clear();
320             focusController()->setFocusedFrame(frame);
321             return true;
322         }
323         frame = incrementFrame(frame, direction == FindDirectionForward, shouldWrap);
324     } while (frame && frame != startFrame);
325 
326     // Search contents of startFrame, on the other side of the selection that we did earlier.
327     // We cheat a bit and just research with wrap on
328     if (shouldWrap && !startFrame->selection()->isNone()) {
329         bool found = startFrame->findString(target, direction == FindDirectionForward, caseSensitivity == TextCaseSensitive, true, true);
330         focusController()->setFocusedFrame(frame);
331         return found;
332     }
333 
334     return false;
335 }
336 
markAllMatchesForText(const String & target,TextCaseSensitivity caseSensitivity,bool shouldHighlight,unsigned limit)337 unsigned int Page::markAllMatchesForText(const String& target, TextCaseSensitivity caseSensitivity, bool shouldHighlight, unsigned limit)
338 {
339     if (target.isEmpty() || !mainFrame())
340         return 0;
341 
342     unsigned matches = 0;
343 
344     Frame* frame = mainFrame();
345     do {
346         frame->setMarkedTextMatchesAreHighlighted(shouldHighlight);
347         matches += frame->markAllMatchesForText(target, caseSensitivity == TextCaseSensitive, (limit == 0) ? 0 : (limit - matches));
348         frame = incrementFrame(frame, true, false);
349     } while (frame);
350 
351     return matches;
352 }
353 
unmarkAllTextMatches()354 void Page::unmarkAllTextMatches()
355 {
356     if (!mainFrame())
357         return;
358 
359     Frame* frame = mainFrame();
360     do {
361         if (Document* document = frame->document())
362             document->removeMarkers(DocumentMarker::TextMatch);
363         frame = incrementFrame(frame, true, false);
364     } while (frame);
365 }
366 
selection() const367 const Selection& Page::selection() const
368 {
369     return focusController()->focusedOrMainFrame()->selection()->selection();
370 }
371 
setDefersLoading(bool defers)372 void Page::setDefersLoading(bool defers)
373 {
374     if (defers == m_defersLoading)
375         return;
376 
377     m_defersLoading = defers;
378     for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext())
379         frame->loader()->setDefersLoading(defers);
380 }
381 
clearUndoRedoOperations()382 void Page::clearUndoRedoOperations()
383 {
384     m_editorClient->clearUndoRedoOperations();
385 }
386 
inLowQualityImageInterpolationMode() const387 bool Page::inLowQualityImageInterpolationMode() const
388 {
389     return m_inLowQualityInterpolationMode;
390 }
391 
setInLowQualityImageInterpolationMode(bool mode)392 void Page::setInLowQualityImageInterpolationMode(bool mode)
393 {
394     m_inLowQualityInterpolationMode = mode;
395 }
396 
setMediaVolume(float volume)397 void Page::setMediaVolume(float volume)
398 {
399     if (volume < 0 || volume > 1)
400         return;
401 
402     if (m_mediaVolume == volume)
403         return;
404 
405     m_mediaVolume = volume;
406     for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext()) {
407         if (frame->document())
408             frame->document()->mediaVolumeDidChange();
409     }
410 }
411 
userStyleSheetLocationChanged()412 void Page::userStyleSheetLocationChanged()
413 {
414 #if !FRAME_LOADS_USER_STYLESHEET
415     // FIXME: We should provide a way to load other types of URLs than just
416     // file: (e.g., http:, data:).
417     if (m_settings->userStyleSheetLocation().isLocalFile())
418         m_userStyleSheetPath = m_settings->userStyleSheetLocation().fileSystemPath();
419     else
420         m_userStyleSheetPath = String();
421 
422     m_didLoadUserStyleSheet = false;
423     m_userStyleSheet = String();
424     m_userStyleSheetModificationTime = 0;
425 #endif
426 }
427 
userStyleSheet() const428 const String& Page::userStyleSheet() const
429 {
430     if (m_userStyleSheetPath.isEmpty()) {
431         ASSERT(m_userStyleSheet.isEmpty());
432         return m_userStyleSheet;
433     }
434 
435     time_t modTime;
436     if (!getFileModificationTime(m_userStyleSheetPath, modTime)) {
437         // The stylesheet either doesn't exist, was just deleted, or is
438         // otherwise unreadable. If we've read the stylesheet before, we should
439         // throw away that data now as it no longer represents what's on disk.
440         m_userStyleSheet = String();
441         return m_userStyleSheet;
442     }
443 
444     // If the stylesheet hasn't changed since the last time we read it, we can
445     // just return the old data.
446     if (m_didLoadUserStyleSheet && modTime <= m_userStyleSheetModificationTime)
447         return m_userStyleSheet;
448 
449     m_didLoadUserStyleSheet = true;
450     m_userStyleSheet = String();
451     m_userStyleSheetModificationTime = modTime;
452 
453     // FIXME: It would be better to load this asynchronously to avoid blocking
454     // the process, but we will first need to create an asynchronous loading
455     // mechanism that is not tied to a particular Frame. We will also have to
456     // determine what our behavior should be before the stylesheet is loaded
457     // and what should happen when it finishes loading, especially with respect
458     // to when the load event fires, when Document::close is called, and when
459     // layout/paint are allowed to happen.
460     RefPtr<SharedBuffer> data = SharedBuffer::createWithContentsOfFile(m_userStyleSheetPath);
461     if (!data)
462         return m_userStyleSheet;
463 
464     m_userStyleSheet = TextResourceDecoder::create("text/css")->decode(data->data(), data->size());
465 
466     return m_userStyleSheet;
467 }
468 
removeAllVisitedLinks()469 void Page::removeAllVisitedLinks()
470 {
471     if (!allPages)
472         return;
473     HashSet<PageGroup*> groups;
474     HashSet<Page*>::iterator pagesEnd = allPages->end();
475     for (HashSet<Page*>::iterator it = allPages->begin(); it != pagesEnd; ++it) {
476         if (PageGroup* group = (*it)->groupPtr())
477             groups.add(group);
478     }
479     HashSet<PageGroup*>::iterator groupsEnd = groups.end();
480     for (HashSet<PageGroup*>::iterator it = groups.begin(); it != groupsEnd; ++it)
481         (*it)->removeVisitedLinks();
482 }
483 
allVisitedStateChanged(PageGroup * group)484 void Page::allVisitedStateChanged(PageGroup* group)
485 {
486     ASSERT(group);
487     ASSERT(allPages);
488     HashSet<Page*>::iterator pagesEnd = allPages->end();
489     for (HashSet<Page*>::iterator it = allPages->begin(); it != pagesEnd; ++it) {
490         Page* page = *it;
491         if (page->m_group != group)
492             continue;
493         for (Frame* frame = page->m_mainFrame.get(); frame; frame = frame->tree()->traverseNext()) {
494             if (CSSStyleSelector* styleSelector = frame->document()->styleSelector())
495                 styleSelector->allVisitedStateChanged();
496         }
497     }
498 }
499 
visitedStateChanged(PageGroup * group,LinkHash visitedLinkHash)500 void Page::visitedStateChanged(PageGroup* group, LinkHash visitedLinkHash)
501 {
502     ASSERT(group);
503     ASSERT(allPages);
504     HashSet<Page*>::iterator pagesEnd = allPages->end();
505     for (HashSet<Page*>::iterator it = allPages->begin(); it != pagesEnd; ++it) {
506         Page* page = *it;
507         if (page->m_group != group)
508             continue;
509         for (Frame* frame = page->m_mainFrame.get(); frame; frame = frame->tree()->traverseNext()) {
510             if (CSSStyleSelector* styleSelector = frame->document()->styleSelector())
511                 styleSelector->visitedStateChanged(visitedLinkHash);
512         }
513     }
514 }
515 
setDebuggerForAllPages(JSC::Debugger * debugger)516 void Page::setDebuggerForAllPages(JSC::Debugger* debugger)
517 {
518     ASSERT(allPages);
519 
520     HashSet<Page*>::iterator end = allPages->end();
521     for (HashSet<Page*>::iterator it = allPages->begin(); it != end; ++it)
522         (*it)->setDebugger(debugger);
523 }
524 
setDebugger(JSC::Debugger * debugger)525 void Page::setDebugger(JSC::Debugger* debugger)
526 {
527     if (m_debugger == debugger)
528         return;
529 
530     m_debugger = debugger;
531 
532     for (Frame* frame = m_mainFrame.get(); frame; frame = frame->tree()->traverseNext())
533         frame->script()->attachDebugger(m_debugger);
534 }
535 
536 #if ENABLE(DOM_STORAGE)
sessionStorage(bool optionalCreate)537 SessionStorage* Page::sessionStorage(bool optionalCreate)
538 {
539     if (!m_sessionStorage && optionalCreate)
540         m_sessionStorage = SessionStorage::create(this);
541 
542     return m_sessionStorage.get();
543 }
544 
setSessionStorage(PassRefPtr<SessionStorage> newStorage)545 void Page::setSessionStorage(PassRefPtr<SessionStorage> newStorage)
546 {
547     ASSERT(newStorage->page() == this);
548     m_sessionStorage = newStorage;
549 }
550 #endif
551 
pendingUnloadEventCount()552 unsigned Page::pendingUnloadEventCount()
553 {
554     return m_pendingUnloadEventCount;
555 }
556 
changePendingUnloadEventCount(int delta)557 void Page::changePendingUnloadEventCount(int delta)
558 {
559     if (!delta)
560         return;
561     ASSERT( (delta + (int)m_pendingUnloadEventCount) >= 0 );
562 
563     if (m_pendingUnloadEventCount == 0)
564         m_chrome->disableSuddenTermination();
565     else if ((m_pendingUnloadEventCount + delta) == 0)
566         m_chrome->enableSuddenTermination();
567 
568     m_pendingUnloadEventCount += delta;
569     return;
570 }
571 
pendingBeforeUnloadEventCount()572 unsigned Page::pendingBeforeUnloadEventCount()
573 {
574     return m_pendingBeforeUnloadEventCount;
575 }
576 
changePendingBeforeUnloadEventCount(int delta)577 void Page::changePendingBeforeUnloadEventCount(int delta)
578 {
579     if (!delta)
580         return;
581     ASSERT( (delta + (int)m_pendingBeforeUnloadEventCount) >= 0 );
582 
583     if (m_pendingBeforeUnloadEventCount == 0)
584         m_chrome->disableSuddenTermination();
585     else if ((m_pendingBeforeUnloadEventCount + delta) == 0)
586         m_chrome->enableSuddenTermination();
587 
588     m_pendingBeforeUnloadEventCount += delta;
589     return;
590 }
591 
592 #if ENABLE(WML)
wmlPageState()593 WMLPageState* Page::wmlPageState()
594 {
595     if (!m_wmlPageState)
596         m_wmlPageState.set(new WMLPageState(this));
597     return m_wmlPageState.get();
598 }
599 #endif
600 
setCustomHTMLTokenizerTimeDelay(double customHTMLTokenizerTimeDelay)601 void Page::setCustomHTMLTokenizerTimeDelay(double customHTMLTokenizerTimeDelay)
602 {
603     if (customHTMLTokenizerTimeDelay < 0) {
604         m_customHTMLTokenizerTimeDelay = -1;
605         return;
606     }
607     m_customHTMLTokenizerTimeDelay = customHTMLTokenizerTimeDelay;
608 }
609 
setCustomHTMLTokenizerChunkSize(int customHTMLTokenizerChunkSize)610 void Page::setCustomHTMLTokenizerChunkSize(int customHTMLTokenizerChunkSize)
611 {
612     if (customHTMLTokenizerChunkSize < 0) {
613         m_customHTMLTokenizerChunkSize = -1;
614         return;
615     }
616     m_customHTMLTokenizerChunkSize = customHTMLTokenizerChunkSize;
617 }
618 
setMemoryCacheClientCallsEnabled(bool enabled)619 void Page::setMemoryCacheClientCallsEnabled(bool enabled)
620 {
621     if (m_areMemoryCacheClientCallsEnabled == enabled)
622         return;
623 
624     m_areMemoryCacheClientCallsEnabled = enabled;
625     if (!enabled)
626         return;
627 
628     for (RefPtr<Frame> frame = mainFrame(); frame; frame = frame->tree()->traverseNext())
629         frame->loader()->tellClientAboutPastMemoryCacheLoads();
630 }
631 
632 } // namespace WebCore
633