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