2011-04-25 Pavel Feldman <pfeldman@google.com>
[WebKit-https.git] / Source / WebCore / inspector / InspectorPageAgent.cpp
1 /*
2  * Copyright (C) 2011 Google 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 are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32
33 #include "InspectorPageAgent.h"
34
35 #if ENABLE(INSPECTOR)
36
37 #include "Base64.h"
38 #include "CachedResourceLoader.h"
39 #include "Cookie.h"
40 #include "CookieJar.h"
41 #include "Document.h"
42 #include "DocumentLoader.h"
43 #include "Frame.h"
44 #include "FrameLoadRequest.h"
45 #include "HTMLFrameOwnerElement.h"
46 #include "HTMLNames.h"
47 #include "InjectedScriptManager.h"
48 #include "InspectorFrontend.h"
49 #include "InspectorValues.h"
50 #include "InstrumentingAgents.h"
51 #include "MemoryCache.h"
52 #include "Page.h"
53 #include "ScriptObject.h"
54 #include "SharedBuffer.h"
55 #include "TextEncoding.h"
56 #include "UserGestureIndicator.h"
57 #include "WindowFeatures.h"
58
59 #include <wtf/CurrentTime.h>
60 #include <wtf/ListHashSet.h>
61
62 namespace WebCore {
63
64 PassOwnPtr<InspectorPageAgent> InspectorPageAgent::create(InstrumentingAgents* instrumentingAgents, Page* page, InjectedScriptManager* injectedScriptManager)
65 {
66     return adoptPtr(new InspectorPageAgent(instrumentingAgents, page, injectedScriptManager));
67 }
68
69 void InspectorPageAgent::resourceContent(ErrorString* errorString, Frame* frame, const KURL& url, String* result)
70 {
71     if (!frame) {
72         *errorString = "No frame to get resource content for";
73         return;
74     }
75
76     String textEncodingName;
77     RefPtr<SharedBuffer> buffer = InspectorPageAgent::resourceData(frame, url, &textEncodingName);
78
79     if (buffer) {
80         TextEncoding encoding(textEncodingName);
81         if (!encoding.isValid())
82             encoding = WindowsLatin1Encoding();
83         *result = encoding.decode(buffer->data(), buffer->size());
84         return;
85     }
86     *errorString = "No resource with given URL found";
87 }
88
89 void InspectorPageAgent::resourceContentBase64(ErrorString* errorString, Frame* frame, const KURL& url, String* result)
90 {
91     String textEncodingName;
92     RefPtr<SharedBuffer> data = InspectorPageAgent::resourceData(frame, url, &textEncodingName);
93     if (!data) {
94         *result = String();
95         *errorString = "No resource with given URL found";
96         return;
97     }
98
99     *result = base64Encode(data->data(), data->size());
100 }
101
102 PassRefPtr<SharedBuffer> InspectorPageAgent::resourceData(Frame* frame, const KURL& url, String* textEncodingName)
103 {
104     FrameLoader* frameLoader = frame->loader();
105     DocumentLoader* loader = frameLoader->documentLoader();
106     if (equalIgnoringFragmentIdentifier(url, loader->url())) {
107         *textEncodingName = frame->document()->inputEncoding();
108         return frameLoader->documentLoader()->mainResourceData();
109     }
110
111     CachedResource* cachedResource = InspectorPageAgent::cachedResource(frame, url);
112     if (!cachedResource)
113         return 0;
114
115     // Zero-sized resources don't have data at all -- so fake the empty buffer, insted of indicating error by returning 0.
116     if (!cachedResource->encodedSize())
117         return SharedBuffer::create();
118
119     if (cachedResource->isPurgeable()) {
120         // If the resource is purgeable then make it unpurgeable to get
121         // get its data. This might fail, in which case we return an
122         // empty String.
123         // FIXME: should we do something else in the case of a purged
124         // resource that informs the user why there is no data in the
125         // inspector?
126         if (!cachedResource->makePurgeable(false))
127             return 0;
128     }
129
130     *textEncodingName = cachedResource->encoding();
131     return cachedResource->data();
132 }
133
134 CachedResource* InspectorPageAgent::cachedResource(Frame* frame, const KURL& url)
135 {
136     CachedResource* cachedResource = frame->document()->cachedResourceLoader()->cachedResource(url);
137     if (!cachedResource)
138         cachedResource = memoryCache()->resourceForURL(url);
139     return cachedResource;
140 }
141
142 String InspectorPageAgent::resourceTypeString(InspectorPageAgent::ResourceType resourceType)
143 {
144     switch (resourceType) {
145     case DocumentResource:
146         return "Document";
147     case ImageResource:
148         return "Image";
149     case FontResource:
150         return "Font";
151     case StylesheetResource:
152         return "Stylesheet";
153     case ScriptResource:
154         return "Script";
155     case XHRResource:
156         return "XHR";
157     case WebSocketResource:
158         return "WebSocket";
159     case OtherResource:
160         return "Other";
161     }
162     return "Other";
163 }
164
165 InspectorPageAgent::ResourceType InspectorPageAgent::cachedResourceType(const CachedResource& cachedResource)
166 {
167     switch (cachedResource.type()) {
168     case CachedResource::ImageResource:
169         return InspectorPageAgent::ImageResource;
170     case CachedResource::FontResource:
171         return InspectorPageAgent::FontResource;
172     case CachedResource::CSSStyleSheet:
173         // Fall through.
174 #if ENABLE(XSLT)
175     case CachedResource::XSLStyleSheet:
176 #endif
177         return InspectorPageAgent::StylesheetResource;
178     case CachedResource::Script:
179         return InspectorPageAgent::ScriptResource;
180     default:
181         break;
182     }
183     return InspectorPageAgent::OtherResource;
184 }
185
186 String InspectorPageAgent::cachedResourceTypeString(const CachedResource& cachedResource)
187 {
188     return resourceTypeString(cachedResourceType(cachedResource));
189 }
190
191 InspectorPageAgent::InspectorPageAgent(InstrumentingAgents* instrumentingAgents, Page* page, InjectedScriptManager* injectedScriptManager)
192     : m_instrumentingAgents(instrumentingAgents)
193     , m_page(page)
194     , m_injectedScriptManager(injectedScriptManager)
195     , m_frontend(0)
196 {
197 }
198
199 void InspectorPageAgent::setFrontend(InspectorFrontend* frontend)
200 {
201     m_frontend = frontend->page();
202     m_instrumentingAgents->setInspectorPageAgent(this);
203
204     // Initialize Web Inspector title.
205     m_frontend->inspectedURLChanged(m_page->mainFrame()->document()->url().string());
206 }
207
208 void InspectorPageAgent::clearFrontend()
209 {
210     m_instrumentingAgents->setInspectorPageAgent(0);
211     m_frontend = 0;
212 }
213
214 void InspectorPageAgent::addScriptToEvaluateOnLoad(ErrorString*, const String& source)
215 {
216     m_scriptsToEvaluateOnLoad.append(source);
217 }
218
219 void InspectorPageAgent::removeAllScriptsToEvaluateOnLoad(ErrorString*)
220 {
221     m_scriptsToEvaluateOnLoad.clear();
222 }
223
224 void InspectorPageAgent::reload(ErrorString*, const bool* const optionalIgnoreCache)
225 {
226     m_page->mainFrame()->loader()->reload(optionalIgnoreCache ? *optionalIgnoreCache : false);
227 }
228
229 void InspectorPageAgent::open(ErrorString*, const String& url, const bool* const inNewWindow)
230 {
231     Frame* mainFrame = m_page->mainFrame();
232     Frame* frame;
233     if (inNewWindow && *inNewWindow) {
234         FrameLoadRequest request(mainFrame->document()->securityOrigin(), ResourceRequest(), "_blank");
235
236         bool created;
237         WindowFeatures windowFeatures;
238         frame = WebCore::createWindow(mainFrame, mainFrame, request, windowFeatures, created);
239         if (!frame)
240             return;
241
242         frame->loader()->setOpener(mainFrame);
243         frame->page()->setOpenedByDOM();
244     } else
245         frame = mainFrame;
246
247     UserGestureIndicator indicator(DefinitelyProcessingUserGesture);
248     frame->loader()->changeLocation(mainFrame->document()->securityOrigin(), frame->loader()->completeURL(url), "", false, false);
249 }
250
251 static PassRefPtr<InspectorObject> buildObjectForCookie(const Cookie& cookie)
252 {
253     RefPtr<InspectorObject> value = InspectorObject::create();
254     value->setString("name", cookie.name);
255     value->setString("value", cookie.value);
256     value->setString("domain", cookie.domain);
257     value->setString("path", cookie.path);
258     value->setNumber("expires", cookie.expires);
259     value->setNumber("size", (cookie.name.length() + cookie.value.length()));
260     value->setBoolean("httpOnly", cookie.httpOnly);
261     value->setBoolean("secure", cookie.secure);
262     value->setBoolean("session", cookie.session);
263     return value;
264 }
265
266 static PassRefPtr<InspectorArray> buildArrayForCookies(ListHashSet<Cookie>& cookiesList)
267 {
268     RefPtr<InspectorArray> cookies = InspectorArray::create();
269
270     ListHashSet<Cookie>::iterator end = cookiesList.end();
271     ListHashSet<Cookie>::iterator it = cookiesList.begin();
272     for (int i = 0; it != end; ++it, i++)
273         cookies->pushObject(buildObjectForCookie(*it));
274
275     return cookies;
276 }
277
278 void InspectorPageAgent::getCookies(ErrorString*, RefPtr<InspectorArray>* cookies, WTF::String* cookiesString)
279 {
280     // If we can get raw cookies.
281     ListHashSet<Cookie> rawCookiesList;
282
283     // If we can't get raw cookies - fall back to String representation
284     String stringCookiesList;
285
286     // Return value to getRawCookies should be the same for every call because
287     // the return value is platform/network backend specific, and the call will
288     // always return the same true/false value.
289     bool rawCookiesImplemented = false;
290
291     for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext(mainFrame())) {
292         Document* document = frame->document();
293         const CachedResourceLoader::DocumentResourceMap& allResources = document->cachedResourceLoader()->allCachedResources();
294         CachedResourceLoader::DocumentResourceMap::const_iterator end = allResources.end();
295         for (CachedResourceLoader::DocumentResourceMap::const_iterator it = allResources.begin(); it != end; ++it) {
296             Vector<Cookie> docCookiesList;
297             rawCookiesImplemented = getRawCookies(document, KURL(ParsedURLString, it->second->url()), docCookiesList);
298
299             if (!rawCookiesImplemented) {
300                 // FIXME: We need duplication checking for the String representation of cookies.
301                 ExceptionCode ec = 0;
302                 stringCookiesList += document->cookie(ec);
303                 // Exceptions are thrown by cookie() in sandboxed frames. That won't happen here
304                 // because "document" is the document of the main frame of the page.
305                 ASSERT(!ec);
306             } else {
307                 int cookiesSize = docCookiesList.size();
308                 for (int i = 0; i < cookiesSize; i++) {
309                     if (!rawCookiesList.contains(docCookiesList[i]))
310                         rawCookiesList.add(docCookiesList[i]);
311                 }
312             }
313         }
314     }
315
316     if (rawCookiesImplemented)
317         *cookies = buildArrayForCookies(rawCookiesList);
318     else
319         *cookiesString = stringCookiesList;
320 }
321
322 void InspectorPageAgent::deleteCookie(ErrorString*, const String& cookieName, const String& domain)
323 {
324     for (Frame* frame = m_page->mainFrame(); frame; frame = frame->tree()->traverseNext(m_page->mainFrame())) {
325         Document* document = frame->document();
326         if (document->url().host() != domain)
327             continue;
328         const CachedResourceLoader::DocumentResourceMap& allResources = document->cachedResourceLoader()->allCachedResources();
329         CachedResourceLoader::DocumentResourceMap::const_iterator end = allResources.end();
330         for (CachedResourceLoader::DocumentResourceMap::const_iterator it = allResources.begin(); it != end; ++it)
331             WebCore::deleteCookie(document, KURL(ParsedURLString, it->second->url()), cookieName);
332     }
333 }
334
335 void InspectorPageAgent::getResourceTree(ErrorString*, RefPtr<InspectorObject>* object)
336 {
337     *object = buildObjectForFrameTree(m_page->mainFrame());
338 }
339
340 void InspectorPageAgent::getResourceContent(ErrorString* errorString, const String& frameId, const String& url, const bool* const optionalBase64Encode, String* content)
341 {
342     Frame* frame = frameForId(frameId);
343     if (!frame) {
344         *errorString = "No frame for given id found";
345         return;
346     }
347     if (optionalBase64Encode ? *optionalBase64Encode : false)
348         InspectorPageAgent::resourceContentBase64(errorString, frame, KURL(ParsedURLString, url), content);
349     else
350         InspectorPageAgent::resourceContent(errorString, frame, KURL(ParsedURLString, url), content);
351 }
352
353 void InspectorPageAgent::restore()
354 {
355     m_frontend->inspectedURLChanged(mainFrame()->document()->url().string());
356 }
357
358 void InspectorPageAgent::domContentEventFired()
359 {
360      m_frontend->domContentEventFired(currentTime());
361 }
362
363 void InspectorPageAgent::loadEventFired()
364 {
365      m_frontend->loadEventFired(currentTime());
366 }
367
368 void InspectorPageAgent::frameNavigated(DocumentLoader* loader)
369 {
370     m_frontend->frameNavigated(buildObjectForFrame(loader->frame()), loaderId(loader));
371     m_frontend->inspectedURLChanged(loader->url().string());
372 }
373
374 void InspectorPageAgent::frameDetached(Frame* frame)
375 {
376     m_frontend->frameDetached(frameId(frame));
377 }
378
379 void InspectorPageAgent::didClearWindowObjectInWorld(Frame* frame, DOMWrapperWorld* world)
380 {
381     if (world != mainThreadNormalWorld())
382         return;
383
384     if (frame == m_page->mainFrame())
385         m_injectedScriptManager->discardInjectedScripts();
386
387     if (m_scriptsToEvaluateOnLoad.size()) {
388         ScriptState* scriptState = mainWorldScriptState(frame);
389         for (Vector<String>::iterator it = m_scriptsToEvaluateOnLoad.begin();
390              it != m_scriptsToEvaluateOnLoad.end(); ++it) {
391             m_injectedScriptManager->injectScript(*it, scriptState);
392         }
393     }
394 }
395
396 Frame* InspectorPageAgent::mainFrame()
397 {
398     return m_page->mainFrame();
399 }
400
401 static String pointerAsId(void* pointer)
402 {
403     unsigned long long address = reinterpret_cast<uintptr_t>(pointer);
404     // We want 0 to be "", so that JavaScript checks for if (frameId) worked.
405     return String::format("%.0llX", address);
406 }
407
408 Frame* InspectorPageAgent::frameForId(const String& frameId)
409 {
410     Frame* mainFrame = m_page->mainFrame();
411     for (Frame* frame = mainFrame; frame; frame = frame->tree()->traverseNext(mainFrame)) {
412         if (pointerAsId(frame) == frameId)
413             return frame;
414     }
415     return 0;
416 }
417
418 String InspectorPageAgent::frameId(Frame* frame)
419 {
420     return pointerAsId(frame);
421 }
422
423 String InspectorPageAgent::loaderId(DocumentLoader* loader)
424 {
425     return pointerAsId(loader);
426 }
427
428 PassRefPtr<InspectorObject> InspectorPageAgent::buildObjectForFrame(Frame* frame)
429 {
430     RefPtr<InspectorObject> frameObject = InspectorObject::create();
431     frameObject->setString("id", frameId(frame));
432     frameObject->setString("parentId", frameId(frame->tree()->parent()));
433     if (frame->ownerElement()) {
434         String name = frame->ownerElement()->getAttribute(HTMLNames::nameAttr);
435         if (name.isEmpty())
436             name = frame->ownerElement()->getAttribute(HTMLNames::idAttr);
437         frameObject->setString("name", name);
438     }
439     frameObject->setString("url", frame->document()->url().string());
440     frameObject->setString("loaderId", loaderId(frame->loader()->documentLoader()));
441
442     return frameObject;
443 }
444
445 PassRefPtr<InspectorObject> InspectorPageAgent::buildObjectForFrameTree(Frame* frame)
446 {
447     RefPtr<InspectorObject> result = InspectorObject::create();
448     RefPtr<InspectorObject> frameObject = buildObjectForFrame(frame);
449     result->setObject("frame", frameObject);
450
451     RefPtr<InspectorArray> subresources = InspectorArray::create();
452     result->setArray("resources", subresources);
453     const CachedResourceLoader::DocumentResourceMap& allResources = frame->document()->cachedResourceLoader()->allCachedResources();
454     CachedResourceLoader::DocumentResourceMap::const_iterator end = allResources.end();
455     for (CachedResourceLoader::DocumentResourceMap::const_iterator it = allResources.begin(); it != end; ++it) {
456         CachedResource* cachedResource = it->second.get();
457         RefPtr<InspectorObject> resourceObject = InspectorObject::create();
458         resourceObject->setString("url", cachedResource->url());
459         resourceObject->setString("type", cachedResourceTypeString(*cachedResource));
460         subresources->pushValue(resourceObject);
461     }
462
463     RefPtr<InspectorArray> childrenArray;
464     for (Frame* child = frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) {
465         if (!childrenArray) {
466             childrenArray = InspectorArray::create();
467             result->setArray("childFrames", childrenArray);
468         }
469         childrenArray->pushObject(buildObjectForFrameTree(child));
470     }
471     return result;
472 }
473
474 } // namespace WebCore
475
476 #endif // ENABLE(INSPECTOR)