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