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