Update untranslated text
[framework/web/webkit-efl.git] / Source / WebKit2 / UIProcess / WebProcessProxy.cpp
1 /*
2  * Copyright (C) 2010, 2011 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "WebProcessProxy.h"
28
29 #include "DataReference.h"
30 #include "PluginInfoStore.h"
31 #include "PluginProcessManager.h"
32 #include "TextChecker.h"
33 #include "TextCheckerState.h"
34 #include "WebBackForwardListItem.h"
35 #include "WebContext.h"
36 #include "WebNavigationDataStore.h"
37 #include "WebNotificationManagerProxy.h"
38 #include "WebPageProxy.h"
39 #include "WebProcessMessages.h"
40 #include "WebProcessProxyMessages.h"
41 #include <WebCore/KURL.h>
42 #include <stdio.h>
43 #include <wtf/text/CString.h>
44 #include <wtf/text/WTFString.h>
45
46 using namespace WebCore;
47 using namespace std;
48
49 #ifdef MESSAGE_CHECK_URL
50 #undef MESSAGE_CHECK_URL
51 #endif
52
53 #define MESSAGE_CHECK_URL(url) MESSAGE_CHECK_BASE(checkURLReceivedFromWebProcess(url), connection())
54
55 namespace WebKit {
56
57 template<typename HashMap>
58 static inline bool isGoodKey(const typename HashMap::KeyType& key)
59 {
60     return key != HashTraits<typename HashMap::KeyType>::emptyValue() && !HashTraits<typename HashMap::KeyType>::isDeletedValue(key);
61 }
62
63 static uint64_t generatePageID()
64 {
65     static uint64_t uniquePageID = 1;
66     return uniquePageID++;
67 }
68
69 PassRefPtr<WebProcessProxy> WebProcessProxy::create(PassRefPtr<WebContext> context)
70 {
71     return adoptRef(new WebProcessProxy(context));
72 }
73
74 WebProcessProxy::WebProcessProxy(PassRefPtr<WebContext> context)
75     : m_responsivenessTimer(this)
76     , m_context(context)
77     , m_mayHaveUniversalFileReadSandboxExtension(false)
78 #if ENABLE(TIZEN_WEBKIT2_TILED_AC_SHARED_PLATFORM_SURFACE)
79     , m_platformSurfaceTexturePool(adoptPtr(new PlatformSurfaceTexturePool()))
80 #endif
81 #if ENABLE(TIZEN_WEBKIT2_MEMORY_SAVING_MODE)
82     , m_memorySavingModeEnabled(false)
83 #endif
84 {
85     connect();
86 }
87
88 WebProcessProxy::~WebProcessProxy()
89 {
90     if (m_connection)
91         m_connection->invalidate();
92     
93     for (size_t i = 0; i < m_pendingMessages.size(); ++i)
94         m_pendingMessages[i].first.releaseArguments();
95
96     if (m_processLauncher) {
97         m_processLauncher->invalidate();
98         m_processLauncher = 0;
99     }
100
101     if (m_threadLauncher) {
102         m_threadLauncher->invalidate();
103         m_threadLauncher = 0;
104     }
105 }
106
107 void WebProcessProxy::connect()
108 {
109     if (m_context->processModel() == ProcessModelSharedSecondaryThread) {
110         ASSERT(!m_threadLauncher);
111         m_threadLauncher = ThreadLauncher::create(this);
112     } else {
113         ASSERT(!m_processLauncher);
114
115         ProcessLauncher::LaunchOptions launchOptions;
116         launchOptions.processType = ProcessLauncher::WebProcess;
117
118 #if PLATFORM(MAC)
119         // We want the web process to match the architecture of the UI process.
120         launchOptions.architecture = ProcessLauncher::LaunchOptions::MatchCurrentArchitecture;
121         launchOptions.executableHeap = false;
122 #endif
123 #ifndef NDEBUG
124         const char* webProcessCmdPrefix = getenv("WEB_PROCESS_CMD_PREFIX");
125         if (webProcessCmdPrefix && *webProcessCmdPrefix)
126             launchOptions.processCmdPrefix = String::fromUTF8(webProcessCmdPrefix);
127 #endif
128         m_processLauncher = ProcessLauncher::create(this, launchOptions);
129     }
130 }
131
132 void WebProcessProxy::disconnect()
133 {
134     if (m_connection) {
135         m_connection->connection()->removeQueueClient(this);
136         m_connection->invalidate();
137         m_connection = nullptr;
138     }
139
140     m_responsivenessTimer.stop();
141
142     Vector<RefPtr<WebFrameProxy> > frames;
143     copyValuesToVector(m_frameMap, frames);
144
145     for (size_t i = 0, size = frames.size(); i < size; ++i)
146         frames[i]->disconnect();
147     m_frameMap.clear();
148
149     m_context->disconnectProcess(this);
150 }
151
152 bool WebProcessProxy::sendMessage(CoreIPC::MessageID messageID, PassOwnPtr<CoreIPC::ArgumentEncoder> arguments, unsigned messageSendFlags)
153 {
154     // If we're waiting for the web process to launch, we need to stash away the messages so we can send them once we have
155     // a CoreIPC connection.
156     if (isLaunching()) {
157         m_pendingMessages.append(make_pair(CoreIPC::Connection::OutgoingMessage(messageID, arguments), messageSendFlags));
158         return true;
159     }
160
161     // If the web process has exited, m_connection will be null here.
162     if (!m_connection)
163         return false;
164
165     return connection()->sendMessage(messageID, arguments, messageSendFlags);
166 }
167
168 bool WebProcessProxy::isLaunching() const
169 {
170     if (m_processLauncher)
171         return m_processLauncher->isLaunching();
172     if (m_threadLauncher)
173         return m_threadLauncher->isLaunching();
174
175     return false;
176 }
177
178 void WebProcessProxy::terminate()
179 {
180     if (m_processLauncher)
181         m_processLauncher->terminateProcess();
182 }
183
184 WebPageProxy* WebProcessProxy::webPage(uint64_t pageID) const
185 {
186     return m_pageMap.get(pageID);
187 }
188
189 PassRefPtr<WebPageProxy> WebProcessProxy::createWebPage(PageClient* pageClient, WebContext* context, WebPageGroup* pageGroup)
190 {
191     ASSERT(context->process() == this);
192
193 #if ENABLE(TIZEN_WEBKIT2_TILED_AC_SHARED_PLATFORM_SURFACE)
194     if (!m_platformSurfaceTexturePool)
195         m_platformSurfaceTexturePool = adoptPtr(new PlatformSurfaceTexturePool());
196 #endif
197
198     uint64_t pageID = generatePageID();
199     RefPtr<WebPageProxy> webPage = WebPageProxy::create(pageClient, this, pageGroup, pageID);
200     m_pageMap.set(pageID, webPage.get());
201     return webPage.release();
202 }
203
204 void WebProcessProxy::addExistingWebPage(WebPageProxy* webPage, uint64_t pageID)
205 {
206     m_pageMap.set(pageID, webPage);
207 }
208
209 void WebProcessProxy::removeWebPage(uint64_t pageID)
210 {
211     m_pageMap.remove(pageID);
212
213 #if ENABLE(TIZEN_WEBKIT2_TILED_AC_SHARED_PLATFORM_SURFACE)
214     if (m_pageMap.size() == 0)
215         m_platformSurfaceTexturePool.release();
216 #endif
217 }
218
219 WebBackForwardListItem* WebProcessProxy::webBackForwardItem(uint64_t itemID) const
220 {
221     return m_backForwardListItemMap.get(itemID).get();
222 }
223
224 void WebProcessProxy::registerNewWebBackForwardListItem(WebBackForwardListItem* item)
225 {
226     // This item was just created by the UIProcess and is being added to the map for the first time
227     // so we should not already have an item for this ID.
228     ASSERT(!m_backForwardListItemMap.contains(item->itemID()));
229
230     m_backForwardListItemMap.set(item->itemID(), item);
231 }
232
233 void WebProcessProxy::assumeReadAccessToBaseURL(const String& urlString)
234 {
235     KURL url(KURL(), urlString);
236     if (!url.isLocalFile())
237         return;
238
239     // There's a chance that urlString does not point to a directory.
240     // Get url's base URL to add to m_localPathsWithAssumedReadAccess.
241     KURL baseURL(KURL(), url.baseAsString());
242     
243     // Client loads an alternate string. This doesn't grant universal file read, but the web process is assumed
244     // to have read access to this directory already.
245     m_localPathsWithAssumedReadAccess.add(baseURL.fileSystemPath());
246 }
247
248 bool WebProcessProxy::checkURLReceivedFromWebProcess(const String& urlString)
249 {
250     return checkURLReceivedFromWebProcess(KURL(KURL(), urlString));
251 }
252
253 bool WebProcessProxy::checkURLReceivedFromWebProcess(const KURL& url)
254 {
255     // FIXME: Consider checking that the URL is valid. Currently, WebProcess sends invalid URLs in many cases, but it probably doesn't have good reasons to do that.
256
257     // Any other non-file URL is OK.
258     if (!url.isLocalFile())
259         return true;
260
261     // Any file URL is also OK if we've loaded a file URL through API before, granting universal read access.
262     if (m_mayHaveUniversalFileReadSandboxExtension)
263         return true;
264
265     // If we loaded a string with a file base URL before, loading resources from that subdirectory is fine.
266     // There are no ".." components, because all URLs received from WebProcess are parsed with KURL, which removes those.
267     String path = url.fileSystemPath();
268     for (HashSet<String>::const_iterator iter = m_localPathsWithAssumedReadAccess.begin(); iter != m_localPathsWithAssumedReadAccess.end(); ++iter) {
269         if (path.startsWith(*iter))
270             return true;
271     }
272
273     // Items in back/forward list have been already checked.
274     // One case where we don't have sandbox extensions for file URLs in b/f list is if the list has been reinstated after a crash or a browser restart.
275     for (WebBackForwardListItemMap::iterator iter = m_backForwardListItemMap.begin(), end = m_backForwardListItemMap.end(); iter != end; ++iter) {
276         if (KURL(KURL(), iter->second->url()).fileSystemPath() == path)
277             return true;
278         if (KURL(KURL(), iter->second->originalURL()).fileSystemPath() == path)
279             return true;
280     }
281
282     // A Web process that was never asked to load a file URL should not ever ask us to do anything with a file URL.
283     WTFLogAlways("Received an unexpected URL from the web process: '%s'\n", url.string().utf8().data());
284     return false;
285 }
286
287 #if !PLATFORM(MAC)
288 bool WebProcessProxy::fullKeyboardAccessEnabled()
289 {
290     return false;
291 }
292 #endif
293
294 void WebProcessProxy::addBackForwardItem(uint64_t itemID, const String& originalURL, const String& url, const String& title, const CoreIPC::DataReference& backForwardData)
295 {
296     MESSAGE_CHECK_URL(originalURL);
297     MESSAGE_CHECK_URL(url);
298
299     WebBackForwardListItemMap::AddResult result = m_backForwardListItemMap.add(itemID, 0);
300     if (result.isNewEntry) {
301         result.iterator->second = WebBackForwardListItem::create(originalURL, url, title, backForwardData.data(), backForwardData.size(), itemID);
302         return;
303     }
304
305     // Update existing item.
306     result.iterator->second->setOriginalURL(originalURL);
307     result.iterator->second->setURL(url);
308     result.iterator->second->setTitle(title);
309     result.iterator->second->setBackForwardData(backForwardData.data(), backForwardData.size());
310 }
311
312 #if ENABLE(PLUGIN_PROCESS)
313 void WebProcessProxy::getPluginProcessConnection(const String& pluginPath, PassRefPtr<Messages::WebProcessProxy::GetPluginProcessConnection::DelayedReply> reply)
314 {
315     PluginProcessManager::shared().getPluginProcessConnection(context()->pluginInfoStore(), pluginPath, reply);
316 }
317
318 void WebProcessProxy::pluginSyncMessageSendTimedOut(const String& pluginPath)
319 {
320     PluginProcessManager::shared().pluginSyncMessageSendTimedOut(pluginPath);
321 }
322 #endif
323
324 void WebProcessProxy::didReceiveMessage(CoreIPC::Connection* connection, CoreIPC::MessageID messageID, CoreIPC::ArgumentDecoder* arguments)
325 {
326     if (messageID.is<CoreIPC::MessageClassWebProcessProxy>()) {
327         didReceiveWebProcessProxyMessage(connection, messageID, arguments);
328         return;
329     }
330
331     if (messageID.is<CoreIPC::MessageClassWebContext>()
332         || messageID.is<CoreIPC::MessageClassWebContextLegacy>()
333         || messageID.is<CoreIPC::MessageClassDownloadProxy>()
334         || messageID.is<CoreIPC::MessageClassWebApplicationCacheManagerProxy>()
335 #if ENABLE(BATTERY_STATUS)
336         || messageID.is<CoreIPC::MessageClassWebBatteryManagerProxy>()
337 #endif
338         || messageID.is<CoreIPC::MessageClassWebCookieManagerProxy>()
339         || messageID.is<CoreIPC::MessageClassWebDatabaseManagerProxy>()
340         || messageID.is<CoreIPC::MessageClassWebGeolocationManagerProxy>()
341         || messageID.is<CoreIPC::MessageClassWebIconDatabase>()
342         || messageID.is<CoreIPC::MessageClassWebKeyValueStorageManagerProxy>()
343 #if ENABLE(TIZEN_FILE_SYSTEM)
344         || messageID.is<CoreIPC::MessageClassWebLocalFileSystemManagerProxy>()
345 #endif
346         || messageID.is<CoreIPC::MessageClassWebMediaCacheManagerProxy>()
347 #if ENABLE(NETWORK_INFO)
348         || messageID.is<CoreIPC::MessageClassWebNetworkInfoManagerProxy>()
349 #endif
350         || messageID.is<CoreIPC::MessageClassWebNotificationManagerProxy>()
351 #if USE(SOUP)
352         || messageID.is<CoreIPC::MessageClassWebSoupRequestManagerProxy>()
353 #endif
354 #if ENABLE(VIBRATION)
355         || messageID.is<CoreIPC::MessageClassWebVibrationProxy>()
356 #endif
357         || messageID.is<CoreIPC::MessageClassWebResourceCacheManagerProxy>()) {
358         m_context->didReceiveMessage(connection, messageID, arguments);
359         return;
360     }
361
362     uint64_t pageID = arguments->destinationID();
363     if (!pageID)
364         return;
365
366     WebPageProxy* pageProxy = webPage(pageID);
367     if (!pageProxy)
368         return;
369     
370     pageProxy->didReceiveMessage(connection, messageID, arguments);
371 }
372
373 void WebProcessProxy::didReceiveSyncMessage(CoreIPC::Connection* connection, CoreIPC::MessageID messageID, CoreIPC::ArgumentDecoder* arguments, OwnPtr<CoreIPC::ArgumentEncoder>& reply)
374 {
375     if (messageID.is<CoreIPC::MessageClassWebProcessProxy>()) {
376         didReceiveSyncWebProcessProxyMessage(connection, messageID, arguments, reply);
377         return;
378     }
379
380     if (messageID.is<CoreIPC::MessageClassWebContext>() || messageID.is<CoreIPC::MessageClassWebContextLegacy>()
381 #if ENABLE(NETWORK_INFO)
382         || messageID.is<CoreIPC::MessageClassWebNetworkInfoManagerProxy>()
383 #endif
384         || messageID.is<CoreIPC::MessageClassDownloadProxy>() || messageID.is<CoreIPC::MessageClassWebIconDatabase>()) {
385         m_context->didReceiveSyncMessage(connection, messageID, arguments, reply);
386         return;
387     }
388
389     uint64_t pageID = arguments->destinationID();
390     if (!pageID)
391         return;
392     
393     WebPageProxy* pageProxy = webPage(pageID);
394     if (!pageProxy)
395         return;
396     
397     pageProxy->didReceiveSyncMessage(connection, messageID, arguments, reply);
398 }
399
400 void WebProcessProxy::didReceiveMessageOnConnectionWorkQueue(CoreIPC::Connection* connection, CoreIPC::MessageID messageID, CoreIPC::ArgumentDecoder* arguments, bool& didHandleMessage)
401 {
402     if (messageID.is<CoreIPC::MessageClassWebProcessProxy>())
403         didReceiveWebProcessProxyMessageOnConnectionWorkQueue(connection, messageID, arguments, didHandleMessage);
404 }
405
406 void WebProcessProxy::didClose(CoreIPC::Connection*)
407 {
408     // Protect ourselves, as the call to disconnect() below may otherwise cause us
409     // to be deleted before we can finish our work.
410     RefPtr<WebProcessProxy> protect(this);
411
412     Vector<RefPtr<WebPageProxy> > pages;
413     copyValuesToVector(m_pageMap, pages);
414
415     disconnect();
416
417     for (size_t i = 0, size = pages.size(); i < size; ++i)
418         pages[i]->processDidCrash();
419 }
420
421 void WebProcessProxy::didReceiveInvalidMessage(CoreIPC::Connection*, CoreIPC::MessageID messageID)
422 {
423     WTFLogAlways("Received an invalid message from the web process with message ID %x\n", messageID.toInt());
424
425     // Terminate the WebProcesses.
426     terminate();
427 }
428
429 void WebProcessProxy::syncMessageSendTimedOut(CoreIPC::Connection*)
430 {
431 }
432
433 void WebProcessProxy::didBecomeUnresponsive(ResponsivenessTimer*)
434 {
435     Vector<RefPtr<WebPageProxy> > pages;
436     copyValuesToVector(m_pageMap, pages);
437     for (size_t i = 0, size = pages.size(); i < size; ++i)
438         pages[i]->processDidBecomeUnresponsive();
439 }
440
441 void WebProcessProxy::interactionOccurredWhileUnresponsive(ResponsivenessTimer*)
442 {
443     Vector<RefPtr<WebPageProxy> > pages;
444     copyValuesToVector(m_pageMap, pages);
445     for (size_t i = 0, size = pages.size(); i < size; ++i)
446         pages[i]->interactionOccurredWhileProcessUnresponsive();
447 }
448
449 void WebProcessProxy::didBecomeResponsive(ResponsivenessTimer*)
450 {
451     Vector<RefPtr<WebPageProxy> > pages;
452     copyValuesToVector(m_pageMap, pages);
453     for (size_t i = 0, size = pages.size(); i < size; ++i)
454         pages[i]->processDidBecomeResponsive();
455 }
456
457 void WebProcessProxy::didFinishLaunching(ProcessLauncher*, CoreIPC::Connection::Identifier connectionIdentifier)
458 {
459     didFinishLaunching(connectionIdentifier);
460 }
461
462 void WebProcessProxy::didFinishLaunching(ThreadLauncher*, CoreIPC::Connection::Identifier connectionIdentifier)
463 {
464     didFinishLaunching(connectionIdentifier);
465 }
466
467 void WebProcessProxy::didFinishLaunching(CoreIPC::Connection::Identifier connectionIdentifier)
468 {
469     ASSERT(!m_connection);
470     
471     m_connection = WebConnectionToWebProcess::create(this, connectionIdentifier, RunLoop::main());
472     m_connection->connection()->addQueueClient(this);
473     m_connection->connection()->open();
474
475     for (size_t i = 0; i < m_pendingMessages.size(); ++i) {
476         CoreIPC::Connection::OutgoingMessage& outgoingMessage = m_pendingMessages[i].first;
477         unsigned messageSendFlags = m_pendingMessages[i].second;
478         connection()->sendMessage(outgoingMessage.messageID(), adoptPtr(outgoingMessage.arguments()), messageSendFlags);
479     }
480
481     m_pendingMessages.clear();
482
483     // Tell the context that we finished launching.
484     m_context->processDidFinishLaunching(this);
485 }
486
487 WebFrameProxy* WebProcessProxy::webFrame(uint64_t frameID) const
488 {
489     return isGoodKey<WebFrameProxyMap>(frameID) ? m_frameMap.get(frameID).get() : 0;
490 }
491
492 bool WebProcessProxy::canCreateFrame(uint64_t frameID) const
493 {
494     return isGoodKey<WebFrameProxyMap>(frameID) && !m_frameMap.contains(frameID);
495 }
496
497 void WebProcessProxy::frameCreated(uint64_t frameID, WebFrameProxy* frameProxy)
498 {
499     ASSERT(canCreateFrame(frameID));
500     m_frameMap.set(frameID, frameProxy);
501 }
502
503 void WebProcessProxy::didDestroyFrame(uint64_t frameID)
504 {
505     // If the page is closed before it has had the chance to send the DidCreateMainFrame message
506     // back to the UIProcess, then the frameDestroyed message will still be received because it
507     // gets sent directly to the WebProcessProxy.
508     ASSERT(isGoodKey<WebFrameProxyMap>(frameID));
509     m_frameMap.remove(frameID);
510 }
511
512 void WebProcessProxy::disconnectFramesFromPage(WebPageProxy* page)
513 {
514     Vector<RefPtr<WebFrameProxy> > frames;
515     copyValuesToVector(m_frameMap, frames);
516     for (size_t i = 0, size = frames.size(); i < size; ++i) {
517         if (frames[i]->page() == page)
518             frames[i]->disconnect();
519     }
520 }
521
522 size_t WebProcessProxy::frameCountInPage(WebPageProxy* page) const
523 {
524     size_t result = 0;
525     for (HashMap<uint64_t, RefPtr<WebFrameProxy> >::const_iterator iter = m_frameMap.begin(); iter != m_frameMap.end(); ++iter) {
526         if (iter->second->page() == page)
527             ++result;
528     }
529     return result;
530 }
531
532 void WebProcessProxy::shouldTerminate(bool& shouldTerminate)
533 {
534     if (!m_pageMap.isEmpty() || !m_context->shouldTerminate(this)) {
535         shouldTerminate = false;
536         return;
537     }
538
539     shouldTerminate = true;
540
541     // We know that the web process is going to terminate so disconnect it from the context.
542     disconnect();
543 }
544
545 void WebProcessProxy::updateTextCheckerState()
546 {
547     if (canSendMessage())
548         send(Messages::WebProcess::SetTextCheckerState(TextChecker::state()), 0);
549 }
550
551 #if ENABLE(TIZEN_WEBKIT2_TILED_AC_SHARED_PLATFORM_SURFACE)
552 void WebProcessProxy::removePlatformSurfaceTextureFromPool(int platformSurfaceId)
553 {
554     if (m_pageMap.size() > 0)
555         platformSurfaceTexturePool()->removePlatformSurfaceTexture(this, platformSurfaceId);
556 }
557 #endif
558
559 #if ENABLE(TIZEN_WEBKIT2_MEMORY_SAVING_MODE)
560 void WebProcessProxy::setMemorySavingMode(bool mode)
561 {
562     m_memorySavingModeEnabled = mode;
563     if (!mode)
564         return;
565
566     // FIXME: appropriate settings will be added.
567
568 }
569 #endif
570
571 #if ENABLE(TIZEN_WEBKIT2_TILED_AC)
572 void WebProcessProxy::releaseBackingStoreMemory()
573 {
574     Vector<RefPtr<WebPageProxy> > pages;
575     copyValuesToVector(m_pageMap, pages);
576     for (size_t i = 0, size = pages.size(); i < size; ++i)
577         pages[i]->purgeBackingStoresOfInactiveView();
578 }
579 #endif
580 } // namespace WebKit