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