b0d5cbd584cd3dd3e44edce61a16ab57d10520eb
[WebKit-https.git] / WebCore / page / InspectorController.cpp
1 /*
2  * Copyright (C) 2007 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  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer. 
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution. 
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission. 
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30 #include "InspectorController.h"
31
32 #include "CString.h"
33 #include "CachedResource.h"
34 #include "Database.h"
35 #include "DocLoader.h"
36 #include "Document.h"
37 #include "DocumentLoader.h"
38 #include "Element.h"
39 #include "FloatRect.h"
40 #include "Frame.h"
41 #include "FrameLoader.h"
42 #include "FrameTree.h"
43 #include "HTMLFrameOwnerElement.h"
44 #include "InspectorClient.h"
45 #if ENABLE(DATABASE)
46 #include "JSDatabase.h"
47 #endif
48 #include "JSRange.h"
49 #include "Page.h"
50 #include "Range.h"
51 #include "ResourceRequest.h"
52 #include "ResourceResponse.h"
53 #include "Settings.h"
54 #include <wtf/RefCounted.h>
55 #include "SharedBuffer.h"
56 #include "SystemTime.h"
57 #include "TextEncoding.h"
58 #include "TextIterator.h"
59 #include "kjs_dom.h"
60 #include "kjs_proxy.h"
61 #include "kjs_window.h"
62 #include <JavaScriptCore/APICast.h>
63 #include <JavaScriptCore/JSLock.h>
64 #include <JavaScriptCore/JSStringRef.h>
65
66 namespace WebCore {
67
68 static void callSimpleFunction(JSContextRef context, JSObjectRef thisObject, const char* functionName)
69 {
70     ASSERT_ARG(context, context);
71     ASSERT_ARG(thisObject, thisObject);
72
73     JSStringRef string = JSStringCreateWithUTF8CString(functionName);
74     JSObjectRef function = JSValueToObject(context, JSObjectGetProperty(context, thisObject, string, 0), 0);
75     JSStringRelease(string);
76
77     JSObjectCallAsFunction(context, function, thisObject, 0, 0, 0);
78 }
79
80 struct ConsoleMessage {
81     ConsoleMessage(MessageSource s, MessageLevel l, const String& m, unsigned li, const String& u)
82         : source(s)
83         , level(l)
84         , message(m)
85         , line(li)
86         , url(u)
87     {
88     }
89
90     MessageSource source;
91     MessageLevel level;
92     String message;
93     unsigned line;
94     String url;
95 };
96
97 struct InspectorResource : public RefCounted<InspectorResource> {
98     // Keep these in sync with WebInspector.Resource.Type
99     enum Type {
100         Doc,
101         Stylesheet,
102         Image,
103         Font,
104         Script,
105         Other
106     };
107
108     InspectorResource(long long identifier, DocumentLoader* documentLoader, Frame* frame)
109         : identifier(identifier)
110         , loader(documentLoader)
111         , frame(frame)
112         , scriptContext(0)
113         , scriptObject(0)
114         , expectedContentLength(0)
115         , cached(false)
116         , finished(false)
117         , failed(false)
118         , length(0)
119         , responseStatusCode(0)
120         , startTime(-1.0)
121         , responseReceivedTime(-1.0)
122         , endTime(-1.0)
123     {
124     }
125
126     ~InspectorResource()
127     {
128         setScriptObject(0, 0);
129     }
130
131     Type type() const
132     {
133         if (requestURL == loader->requestURL())
134             return Doc;
135
136         FrameLoader* frameLoader = loader->frameLoader();
137         if (!frameLoader)
138             return Other;
139
140         if (requestURL == frameLoader->iconURL())
141             return Image;
142
143         Document* doc = frameLoader->frame()->document();
144         if (!doc)
145             return Other;
146
147         CachedResource* cachedResource = doc->docLoader()->cachedResource(requestURL.url());
148         if (!cachedResource)
149             return Other;
150
151         switch (cachedResource->type()) {
152             case CachedResource::ImageResource:
153                 return Image;
154             case CachedResource::FontResource:
155                 return Font;
156             case CachedResource::CSSStyleSheet:
157 #if ENABLE(XSLT)
158             case CachedResource::XSLStyleSheet:
159 #endif
160                 return Stylesheet;
161             case CachedResource::Script:
162                 return Script;
163             default:
164                 return Other;
165         }
166     }
167
168     void setScriptObject(JSContextRef context, JSObjectRef newScriptObject)
169     {
170         if (scriptContext && scriptObject)
171             JSValueUnprotect(scriptContext, scriptObject);
172
173         scriptObject = newScriptObject;
174         scriptContext = context;
175
176         ASSERT((context && newScriptObject) || (!context && !newScriptObject));
177         if (context && newScriptObject)
178             JSValueProtect(context, newScriptObject);
179     }
180
181     long long identifier;
182     RefPtr<DocumentLoader> loader;
183     RefPtr<Frame> frame;
184     KURL requestURL;
185     HTTPHeaderMap requestHeaderFields;
186     HTTPHeaderMap responseHeaderFields;
187     String mimeType;
188     String suggestedFilename;
189     JSContextRef scriptContext;
190     JSObjectRef scriptObject;
191     long long expectedContentLength;
192     bool cached;
193     bool finished;
194     bool failed;
195     int length;
196     int responseStatusCode;
197     double startTime;
198     double responseReceivedTime;
199     double endTime;
200 };
201
202 #if ENABLE(DATABASE)
203 struct InspectorDatabaseResource : public RefCounted<InspectorDatabaseResource> {
204     InspectorDatabaseResource(Database* database, String domain, String name, String version)
205         : database(database)
206         , domain(domain)
207         , name(name)
208         , version(version)
209         , scriptContext(0)
210         , scriptObject(0)
211     {
212     }
213
214     InspectorDatabaseResource()
215     {
216         setScriptObject(0, 0);
217     }
218
219     void setScriptObject(JSContextRef context, JSObjectRef newScriptObject)
220     {
221         if (scriptContext && scriptObject)
222             JSValueUnprotect(scriptContext, scriptObject);
223
224         scriptObject = newScriptObject;
225         scriptContext = context;
226
227         ASSERT((context && newScriptObject) || (!context && !newScriptObject));
228         if (context && newScriptObject)
229             JSValueProtect(context, newScriptObject);
230     }
231
232     RefPtr<Database> database;
233     String domain;
234     String name;
235     String version;
236     JSContextRef scriptContext;
237     JSObjectRef scriptObject;
238 };
239 #endif
240
241 static JSValueRef addSourceToFrame(JSContextRef ctx, JSObjectRef /*function*/, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* /*exception*/)
242 {
243     JSValueRef undefined = JSValueMakeUndefined(ctx);
244
245     InspectorController* controller = reinterpret_cast<InspectorController*>(JSObjectGetPrivate(thisObject));
246     if (argumentCount < 2 || !controller)
247         return undefined;
248
249     JSValueRef identifierValue = arguments[0];
250     if (!JSValueIsNumber(ctx, identifierValue))
251         return undefined;
252
253     unsigned long identifier = static_cast<unsigned long>(JSValueToNumber(ctx, identifierValue, 0));
254     RefPtr<InspectorResource> resource = controller->resources().get(identifier);
255     ASSERT(resource);
256     if (!resource)
257         return undefined;
258
259     RefPtr<SharedBuffer> buffer;
260     String textEncodingName;
261     if (resource->requestURL == resource->loader->requestURL()) {
262         buffer = resource->loader->mainResourceData();
263         textEncodingName = resource->loader->frame()->document()->inputEncoding();
264     } else {
265         FrameLoader* frameLoader = resource->loader->frameLoader();
266         if (!frameLoader)
267             return undefined;
268
269         Document* doc = frameLoader->frame()->document();
270         if (!doc)
271             return undefined;
272
273         CachedResource* cachedResource = doc->docLoader()->cachedResource(resource->requestURL.url());
274         if (!cachedResource)
275             return undefined;
276
277         buffer = cachedResource->data();
278         textEncodingName = cachedResource->encoding();
279     }
280
281     if (!buffer)
282         return undefined;
283
284     TextEncoding encoding(textEncodingName);
285     if (!encoding.isValid())
286         encoding = WindowsLatin1Encoding();
287     String sourceString = encoding.decode(buffer->data(), buffer->size());
288
289     Node* node = toNode(toJS(arguments[1]));
290     ASSERT(node);
291     if (!node)
292         return undefined;
293
294     if (!node->attached()) {
295         ASSERT_NOT_REACHED();
296         return undefined;
297     }
298
299     ASSERT(node->isElementNode());
300     if (!node->isElementNode())
301         return undefined;
302
303     Element* element = static_cast<Element*>(node);
304     ASSERT(element->isFrameOwnerElement());
305     if (!element->isFrameOwnerElement())
306         return undefined;
307
308     HTMLFrameOwnerElement* frameOwner = static_cast<HTMLFrameOwnerElement*>(element);
309     ASSERT(frameOwner->contentFrame());
310     if (!frameOwner->contentFrame())
311         return undefined;
312
313     FrameLoader* loader = frameOwner->contentFrame()->loader();
314
315     loader->setResponseMIMEType(resource->mimeType);
316     loader->begin();
317     loader->write(sourceString);
318     loader->end();
319
320     return undefined;
321 }
322
323 static JSValueRef getResourceDocumentNode(JSContextRef ctx, JSObjectRef /*function*/, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* /*exception*/)
324 {
325     JSValueRef undefined = JSValueMakeUndefined(ctx);
326
327     InspectorController* controller = reinterpret_cast<InspectorController*>(JSObjectGetPrivate(thisObject));
328     if (!argumentCount || argumentCount > 1 || !controller)
329         return undefined;
330
331     JSValueRef identifierValue = arguments[0];
332     if (!JSValueIsNumber(ctx, identifierValue))
333         return undefined;
334
335     unsigned long identifier = static_cast<unsigned long>(JSValueToNumber(ctx, identifierValue, 0));
336     RefPtr<InspectorResource> resource = controller->resources().get(identifier);
337     ASSERT(resource);
338     if (!resource)
339         return undefined;
340
341     FrameLoader* frameLoader = resource->loader->frameLoader();
342     if (!frameLoader)
343         return undefined;
344
345     Document* document = frameLoader->frame()->document();
346     if (!document)
347         return undefined;
348
349     if (document->isPluginDocument() || document->isImageDocument())
350         return undefined;
351
352     KJS::JSLock lock;
353     JSValueRef documentValue = toRef(toJS(toJS(controller->scriptContext()), document));
354     return documentValue;
355 }
356
357 static JSValueRef highlightDOMNode(JSContextRef context, JSObjectRef /*function*/, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* /*exception*/)
358 {
359     JSValueRef undefined = JSValueMakeUndefined(context);
360
361     InspectorController* controller = reinterpret_cast<InspectorController*>(JSObjectGetPrivate(thisObject));
362     if (argumentCount < 1 || !controller)
363         return undefined;
364
365     Node* node = toNode(toJS(arguments[0]));
366     if (!node)
367         return undefined;
368
369     controller->highlight(node);
370
371     return undefined;
372 }
373
374 static JSValueRef hideDOMNodeHighlight(JSContextRef context, JSObjectRef /*function*/, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* /*exception*/)
375 {
376     JSValueRef undefined = JSValueMakeUndefined(context);
377
378     InspectorController* controller = reinterpret_cast<InspectorController*>(JSObjectGetPrivate(thisObject));
379     if (argumentCount || !controller)
380         return undefined;
381
382     controller->hideHighlight();
383
384     return undefined;
385 }
386
387 static JSValueRef loaded(JSContextRef ctx, JSObjectRef /*function*/, JSObjectRef thisObject, size_t /*argumentCount*/, const JSValueRef[] /*arguments[]*/, JSValueRef* /*exception*/)
388 {
389     InspectorController* controller = reinterpret_cast<InspectorController*>(JSObjectGetPrivate(thisObject));
390     if (!controller)
391         return JSValueMakeUndefined(ctx);
392
393     controller->scriptObjectReady();
394     return JSValueMakeUndefined(ctx);
395 }
396
397 static JSValueRef unloading(JSContextRef ctx, JSObjectRef /*function*/, JSObjectRef thisObject, size_t /*argumentCount*/, const JSValueRef[] /*arguments[]*/, JSValueRef* /*exception*/)
398 {
399     InspectorController* controller = reinterpret_cast<InspectorController*>(JSObjectGetPrivate(thisObject));
400     if (!controller)
401         return JSValueMakeUndefined(ctx);
402
403     controller->close();
404     return JSValueMakeUndefined(ctx);
405 }
406
407 static JSValueRef attach(JSContextRef ctx, JSObjectRef /*function*/, JSObjectRef thisObject, size_t /*argumentCount*/, const JSValueRef[] /*arguments[]*/, JSValueRef* /*exception*/)
408 {
409     InspectorController* controller = reinterpret_cast<InspectorController*>(JSObjectGetPrivate(thisObject));
410     if (!controller)
411         return JSValueMakeUndefined(ctx);
412
413     controller->attachWindow();
414     return JSValueMakeUndefined(ctx);
415 }
416
417 static JSValueRef detach(JSContextRef ctx, JSObjectRef /*function*/, JSObjectRef thisObject, size_t /*argumentCount*/, const JSValueRef[] /*arguments[]*/, JSValueRef* /*exception*/)
418 {
419     InspectorController* controller = reinterpret_cast<InspectorController*>(JSObjectGetPrivate(thisObject));
420     if (!controller)
421         return JSValueMakeUndefined(ctx);
422
423     controller->detachWindow();
424     return JSValueMakeUndefined(ctx);
425 }
426
427 static JSValueRef search(JSContextRef ctx, JSObjectRef /*function*/, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* /*exception*/)
428 {
429     InspectorController* controller = reinterpret_cast<InspectorController*>(JSObjectGetPrivate(thisObject));
430     if (!controller)
431         return JSValueMakeUndefined(ctx);
432
433     if (argumentCount < 2 || !JSValueIsString(ctx, arguments[1]))
434         return JSValueMakeUndefined(ctx);
435
436     Node* node = toNode(toJS(arguments[0]));
437     if (!node)
438         return JSValueMakeUndefined(ctx);
439
440     JSStringRef string = JSValueToStringCopy(ctx, arguments[1], 0);
441     String target(JSStringGetCharactersPtr(string), JSStringGetLength(string));
442     JSStringRelease(string);
443
444     JSObjectRef globalObject = JSContextGetGlobalObject(ctx);
445     JSStringRef constructorString = JSStringCreateWithUTF8CString("Array");
446     JSObjectRef arrayConstructor = JSValueToObject(ctx, JSObjectGetProperty(ctx, globalObject, constructorString, 0), 0);
447     JSStringRelease(constructorString);
448     JSObjectRef array = JSObjectCallAsConstructor(ctx, arrayConstructor, 0, 0, 0);
449
450     JSStringRef pushString = JSStringCreateWithUTF8CString("push");
451     JSValueRef pushValue = JSObjectGetProperty(ctx, array, pushString, 0);
452     JSStringRelease(pushString);
453     JSObjectRef push = JSValueToObject(ctx, pushValue, 0);
454
455     RefPtr<Range> searchRange(rangeOfContents(node));
456
457     int exception = 0;
458     do {
459         RefPtr<Range> resultRange(findPlainText(searchRange.get(), target, true, false));
460         if (resultRange->collapsed(exception))
461             break;
462
463         // A non-collapsed result range can in some funky whitespace cases still not
464         // advance the range's start position (4509328). Break to avoid infinite loop.
465         VisiblePosition newStart = endVisiblePosition(resultRange.get(), DOWNSTREAM);
466         if (newStart == startVisiblePosition(searchRange.get(), DOWNSTREAM))
467             break;
468
469         KJS::JSLock lock;
470         JSValueRef arg0 = toRef(toJS(toJS(ctx), resultRange.get()));
471         JSObjectCallAsFunction(ctx, push, array, 1, &arg0, 0);
472
473         setStart(searchRange.get(), newStart);
474     } while (true);
475
476     return array;
477 }
478
479 #if ENABLE(DATABASE)
480 static JSValueRef databaseTableNames(JSContextRef ctx, JSObjectRef /*function*/, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* /*exception*/)
481 {
482     InspectorController* controller = reinterpret_cast<InspectorController*>(JSObjectGetPrivate(thisObject));
483     if (!controller)
484         return JSValueMakeUndefined(ctx);
485
486     if (argumentCount < 1)
487         return JSValueMakeUndefined(ctx);
488
489     Database* database = toDatabase(toJS(arguments[0]));
490     if (!database)
491         return JSValueMakeUndefined(ctx);
492
493     JSObjectRef global = JSContextGetGlobalObject(ctx);
494     JSStringRef arrayString = JSStringCreateWithUTF8CString("Array");
495     JSObjectRef arrayConstructor = JSValueToObject(ctx, JSObjectGetProperty(ctx, global, arrayString, 0), 0);
496     JSStringRelease(arrayString);
497
498     JSObjectRef result = JSObjectCallAsConstructor(ctx, arrayConstructor, 0, 0, 0);
499
500     JSStringRef pushString = JSStringCreateWithUTF8CString("push");
501     JSObjectRef pushFunction = JSValueToObject(ctx, JSObjectGetProperty(ctx, result, pushString, 0), 0);
502     JSStringRelease(pushString);
503
504     Vector<String> tableNames = database->tableNames();
505     unsigned length = tableNames.size();
506     for (unsigned i = 0; i < length; ++i) {
507         String tableName = tableNames[i];
508         JSStringRef tableNameString = JSStringCreateWithCharacters(tableName.characters(), tableName.length());
509         JSValueRef tableNameValue = JSValueMakeString(ctx, tableNameString);
510         JSStringRelease(tableNameString);
511
512         JSValueRef pushArguments[] = { tableNameValue };
513         JSObjectCallAsFunction(ctx, pushFunction, result, 1, pushArguments, 0);
514     }
515
516     return result;
517 }
518 #endif
519
520 static JSValueRef inspectedWindow(JSContextRef ctx, JSObjectRef /*function*/, JSObjectRef thisObject, size_t /*argumentCount*/, const JSValueRef[] /*arguments[]*/, JSValueRef* /*exception*/)
521 {
522     InspectorController* controller = reinterpret_cast<InspectorController*>(JSObjectGetPrivate(thisObject));
523     if (!controller)
524         return JSValueMakeUndefined(ctx);
525
526     return toRef(KJS::Window::retrieve(controller->inspectedPage()->mainFrame()));
527 }
528
529 static JSValueRef localizedStrings(JSContextRef ctx, JSObjectRef /*function*/, JSObjectRef thisObject, size_t /*argumentCount*/, const JSValueRef[] /*arguments[]*/, JSValueRef* /*exception*/)
530 {
531     InspectorController* controller = reinterpret_cast<InspectorController*>(JSObjectGetPrivate(thisObject));
532     if (!controller)
533         return JSValueMakeUndefined(ctx);
534
535     String url = controller->localizedStringsURL();
536     if (url.isNull())
537         return JSValueMakeNull(ctx);
538
539     JSStringRef urlString = JSStringCreateWithCharacters(url.characters(), url.length());
540     JSValueRef urlValue = JSValueMakeString(ctx, urlString);
541     JSStringRelease(urlString);
542
543     return urlValue;
544 }
545
546 InspectorController::InspectorController(Page* page, InspectorClient* client)
547     : m_inspectedPage(page)
548     , m_client(client)
549     , m_page(0)
550     , m_scriptObject(0)
551     , m_controllerScriptObject(0)
552     , m_scriptContext(0)
553     , m_windowVisible(false)
554     , m_showAfterVisible(FocusedNodeDocumentPanel)
555     , m_nextIdentifier(-2)
556 {
557     ASSERT_ARG(page, page);
558     ASSERT_ARG(client, client);
559 }
560
561 InspectorController::~InspectorController()
562 {
563     m_client->inspectorDestroyed();
564
565     if (m_scriptContext) {
566         JSObjectRef global = JSContextGetGlobalObject(m_scriptContext);
567         JSStringRef controllerProperty = JSStringCreateWithUTF8CString("InspectorController");
568         JSObjectRef controller = JSValueToObject(m_scriptContext, JSObjectGetProperty(m_scriptContext, global, controllerProperty, 0), 0);
569         JSStringRelease(controllerProperty);
570         JSObjectSetPrivate(controller, 0);
571     }
572
573     if (m_page)
574         m_page->setParentInspectorController(0);
575
576     deleteAllValues(m_frameResources);
577     deleteAllValues(m_consoleMessages);
578 }
579
580 bool InspectorController::enabled() const
581 {
582     return m_inspectedPage->settings()->developerExtrasEnabled();
583 }
584
585 String InspectorController::localizedStringsURL()
586 {
587     if (!enabled())
588         return String();
589     return m_client->localizedStringsURL();
590 }
591
592 void InspectorController::inspect(Node* node)
593 {
594     if (!node || !enabled())
595         return;
596
597     show();
598
599     if (node->nodeType() != Node::ELEMENT_NODE && node->nodeType() != Node::DOCUMENT_NODE)
600         node = node->parentNode();
601     m_nodeToFocus = node;
602
603     if (!m_scriptObject) {
604         m_showAfterVisible = FocusedNodeDocumentPanel;
605         return;
606     }
607
608     if (windowVisible())
609         focusNode();
610 }
611
612 void InspectorController::focusNode()
613 {
614     if (!enabled())
615         return;
616
617     ASSERT(m_scriptContext);
618     ASSERT(m_scriptObject);
619     ASSERT(m_nodeToFocus);
620
621     JSValueRef arg0;
622
623     {
624         KJS::JSLock lock;
625         arg0 = toRef(toJS(toJS(m_scriptContext), m_nodeToFocus.get()));
626     }
627
628     m_nodeToFocus = 0;
629
630     JSStringRef functionProperty = JSStringCreateWithUTF8CString("updateFocusedNode");
631     JSObjectRef function = JSValueToObject(m_scriptContext, JSObjectGetProperty(m_scriptContext, m_scriptObject, functionProperty, 0), 0);
632     JSStringRelease(functionProperty);
633     ASSERT(function);
634
635     JSObjectCallAsFunction(m_scriptContext, function, m_scriptObject, 1, &arg0, 0);
636 }
637
638 void InspectorController::highlight(Node* node)
639 {
640     if (!enabled())
641         return;
642     ASSERT_ARG(node, node);
643     m_client->highlight(node);
644 }
645
646 void InspectorController::hideHighlight()
647 {
648     if (!enabled())
649         return;
650     m_client->hideHighlight();
651 }
652
653 bool InspectorController::windowVisible()
654 {
655     return m_windowVisible;
656 }
657
658 void InspectorController::setWindowVisible(bool visible)
659 {
660     if (visible == m_windowVisible)
661         return;
662
663     m_windowVisible = visible;
664
665     if (!m_scriptContext || !m_scriptObject)
666         return;
667
668     if (m_windowVisible) {
669         populateScriptResources();
670         if (m_nodeToFocus)
671             focusNode();
672         if (m_showAfterVisible == ConsolePanel)
673             showConsole();
674         else if (m_showAfterVisible == TimelinePanel)
675             showTimeline();
676     } else {
677         clearScriptResources();
678         clearScriptConsoleMessages();
679         clearDatabaseScriptResources();
680         clearNetworkTimeline();
681     }
682
683     m_showAfterVisible = FocusedNodeDocumentPanel;
684 }
685
686 void InspectorController::addMessageToConsole(MessageSource source, MessageLevel level, const String& message, unsigned lineNumber, const String& sourceID)
687 {
688     if (!enabled())
689         return;
690
691     ConsoleMessage* consoleMessage = new ConsoleMessage(source, level, message, lineNumber, sourceID);
692     m_consoleMessages.append(consoleMessage);
693
694     if (windowVisible())
695         addScriptConsoleMessage(consoleMessage);
696 }
697
698 void InspectorController::attachWindow()
699 {
700     if (!enabled())
701         return;
702     m_client->attachWindow();
703 }
704
705 void InspectorController::detachWindow()
706 {
707     if (!enabled())
708         return;
709     m_client->detachWindow();
710 }
711
712 void InspectorController::windowScriptObjectAvailable()
713 {
714     if (!m_page || !enabled())
715         return;
716
717     m_scriptContext = toRef(m_page->mainFrame()->scriptProxy()->interpreter()->globalExec());
718
719     JSObjectRef global = JSContextGetGlobalObject(m_scriptContext);
720     ASSERT(global);
721
722     static JSStaticFunction staticFunctions[] = {
723         { "addSourceToFrame", addSourceToFrame, kJSPropertyAttributeNone },
724         { "getResourceDocumentNode", getResourceDocumentNode, kJSPropertyAttributeNone },
725         { "highlightDOMNode", highlightDOMNode, kJSPropertyAttributeNone },
726         { "hideDOMNodeHighlight", hideDOMNodeHighlight, kJSPropertyAttributeNone },
727         { "loaded", loaded, kJSPropertyAttributeNone },
728         { "windowUnloading", unloading, kJSPropertyAttributeNone },
729         { "attach", attach, kJSPropertyAttributeNone },
730         { "detach", detach, kJSPropertyAttributeNone },
731         { "search", search, kJSPropertyAttributeNone },
732 #if ENABLE(DATABASE)
733         { "databaseTableNames", databaseTableNames, kJSPropertyAttributeNone },
734 #endif
735         { "inspectedWindow", inspectedWindow, kJSPropertyAttributeNone },
736         { "localizedStringsURL", localizedStrings, kJSPropertyAttributeNone },
737         { 0, 0, 0 }
738     };
739
740     JSClassDefinition inspectorControllerDefinition = {
741         0, kJSClassAttributeNone, "InspectorController", 0, 0, staticFunctions,
742         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
743     };
744
745     JSClassRef controllerClass = JSClassCreate(&inspectorControllerDefinition);
746     ASSERT(controllerClass);
747
748     m_controllerScriptObject = JSObjectMake(m_scriptContext, controllerClass, reinterpret_cast<void*>(this));
749     ASSERT(m_controllerScriptObject);
750
751     JSStringRef controllerObjectString = JSStringCreateWithUTF8CString("InspectorController");
752     JSObjectSetProperty(m_scriptContext, global, controllerObjectString, m_controllerScriptObject, kJSPropertyAttributeNone, 0);
753     JSStringRelease(controllerObjectString);
754 }
755
756 void InspectorController::scriptObjectReady()
757 {
758     ASSERT(m_scriptContext);
759     if (!m_scriptContext)
760         return;
761
762     JSObjectRef global = JSContextGetGlobalObject(m_scriptContext);
763     ASSERT(global);
764
765     JSStringRef inspectorString = JSStringCreateWithUTF8CString("WebInspector");
766     JSValueRef inspectorValue = JSObjectGetProperty(m_scriptContext, global, inspectorString, 0);
767     JSStringRelease(inspectorString);
768
769     ASSERT(inspectorValue);
770     if (!inspectorValue)
771         return;
772
773     m_scriptObject = JSValueToObject(m_scriptContext, inspectorValue, 0);
774     ASSERT(m_scriptObject);
775
776     JSValueProtect(m_scriptContext, m_scriptObject);
777
778     // Make sure our window is visible now that the page loaded
779     m_client->showWindow();
780 }
781
782 void InspectorController::show()
783 {
784     if (!enabled())
785         return;
786
787     if (!m_page) {
788         m_page = m_client->createPage();
789         if (!m_page)
790             return;
791         m_page->setParentInspectorController(this);
792
793         // m_client->showWindow() will be called after the page loads in scriptObjectReady()
794         return;
795     }
796
797     m_client->showWindow();
798 }
799
800 void InspectorController::showConsole()
801 {
802     if (!enabled())
803         return;
804
805     show();
806
807     if (!m_scriptObject) {
808         m_showAfterVisible = ConsolePanel;
809         return;
810     }
811
812     callSimpleFunction(m_scriptContext, m_scriptObject, "showConsole");
813 }
814
815 void InspectorController::showTimeline()
816 {
817     if (!enabled())
818         return;
819
820     show();
821
822     if (!m_scriptObject) {
823         m_showAfterVisible = TimelinePanel;
824         return;
825     }
826
827     callSimpleFunction(m_scriptContext, m_scriptObject, "showTimeline");
828 }
829
830 void InspectorController::close()
831 {
832     if (!enabled())
833         return;
834
835     m_client->closeWindow();
836     if (m_page)
837         m_page->setParentInspectorController(0);
838
839     ASSERT(m_scriptContext && m_scriptObject);
840     JSValueUnprotect(m_scriptContext, m_scriptObject);
841
842     m_page = 0;
843     m_scriptObject = 0;
844     m_scriptContext = 0;
845 }
846
847 static void addHeaders(JSContextRef context, JSObjectRef object, const HTTPHeaderMap& headers)
848 {
849     ASSERT_ARG(context, context);
850     ASSERT_ARG(object, object);
851     
852     HTTPHeaderMap::const_iterator end = headers.end();
853     for (HTTPHeaderMap::const_iterator it = headers.begin(); it != end; ++it) {
854         JSStringRef field = JSStringCreateWithCharacters(it->first.characters(), it->first.length());
855         JSStringRef valueString = JSStringCreateWithCharacters(it->second.characters(), it->second.length());
856         JSValueRef value = JSValueMakeString(context, valueString);
857         JSObjectSetProperty(context, object, field, value, kJSPropertyAttributeNone, 0);
858         JSStringRelease(field);
859         JSStringRelease(valueString);
860     }
861 }
862
863 static JSObjectRef scriptObjectForRequest(JSContextRef context, const InspectorResource* resource)
864 {
865     ASSERT_ARG(context, context);
866
867     JSObjectRef object = JSObjectMake(context, 0, 0);
868     addHeaders(context, object, resource->requestHeaderFields);
869
870     return object;
871 }
872
873 static JSObjectRef scriptObjectForResponse(JSContextRef context, const InspectorResource* resource)
874 {
875     ASSERT_ARG(context, context);
876
877     JSObjectRef object = JSObjectMake(context, 0, 0);
878     addHeaders(context, object, resource->responseHeaderFields);
879
880     return object;
881 }
882
883 JSObjectRef InspectorController::addScriptResource(InspectorResource* resource)
884 {
885     ASSERT_ARG(resource, resource);
886
887     // This happens for pages loaded from the back/forward cache.
888     if (resource->scriptObject)
889         return resource->scriptObject;
890
891     ASSERT(m_scriptContext);
892     ASSERT(m_scriptObject);
893     if (!m_scriptContext || !m_scriptObject)
894         return 0;
895
896     JSStringRef resourceString = JSStringCreateWithUTF8CString("Resource");
897     JSObjectRef resourceConstructor = JSValueToObject(m_scriptContext, JSObjectGetProperty(m_scriptContext, m_scriptObject, resourceString, 0), 0);
898     JSStringRelease(resourceString);
899
900     String urlString = resource->requestURL.url();
901     JSStringRef url = JSStringCreateWithCharacters(urlString.characters(), urlString.length());
902     JSValueRef urlValue = JSValueMakeString(m_scriptContext, url);
903     JSStringRelease(url);
904
905     urlString = resource->requestURL.host();
906     JSStringRef domain = JSStringCreateWithCharacters(urlString.characters(), urlString.length());
907     JSValueRef domainValue = JSValueMakeString(m_scriptContext, domain);
908     JSStringRelease(domain);
909
910     urlString = resource->requestURL.path();
911     JSStringRef path = JSStringCreateWithCharacters(urlString.characters(), urlString.length());
912     JSValueRef pathValue = JSValueMakeString(m_scriptContext, path);
913     JSStringRelease(path);
914
915     urlString = resource->requestURL.lastPathComponent();
916     JSStringRef lastPathComponent = JSStringCreateWithCharacters(urlString.characters(), urlString.length());
917     JSValueRef lastPathComponentValue = JSValueMakeString(m_scriptContext, lastPathComponent);
918     JSStringRelease(lastPathComponent);
919
920     JSValueRef identifier = JSValueMakeNumber(m_scriptContext, resource->identifier);
921     JSValueRef mainResource = JSValueMakeBoolean(m_scriptContext, m_mainResource == resource);
922     JSValueRef cached = JSValueMakeBoolean(m_scriptContext, resource->cached);
923
924     JSValueRef arguments[] = { scriptObjectForRequest(m_scriptContext, resource), urlValue, domainValue, pathValue, lastPathComponentValue, identifier, mainResource, cached };
925     JSObjectRef result = JSObjectCallAsConstructor(m_scriptContext, resourceConstructor, 8, arguments, 0);
926
927     resource->setScriptObject(m_scriptContext, result);
928
929     ASSERT(result);
930
931     JSStringRef addResourceString = JSStringCreateWithUTF8CString("addResource");
932     JSObjectRef addResourceFunction = JSValueToObject(m_scriptContext, JSObjectGetProperty(m_scriptContext, m_scriptObject, addResourceString, 0), 0);
933     JSStringRelease(addResourceString);
934
935     JSValueRef addArguments[] = { result };
936     JSObjectCallAsFunction(m_scriptContext, addResourceFunction, m_scriptObject, 1, addArguments, 0);
937
938     return result;
939 }
940
941 JSObjectRef InspectorController::addAndUpdateScriptResource(InspectorResource* resource)
942 {
943     ASSERT_ARG(resource, resource);
944
945     JSObjectRef scriptResource = addScriptResource(resource);
946     updateScriptResourceResponse(resource);
947     updateScriptResource(resource, resource->length);
948     updateScriptResource(resource, resource->startTime, resource->responseReceivedTime, resource->endTime);
949     updateScriptResource(resource, resource->finished, resource->failed);
950     return scriptResource;
951 }
952
953 void InspectorController::removeScriptResource(InspectorResource* resource)
954 {
955     ASSERT(m_scriptContext);
956     ASSERT(m_scriptObject);
957     if (!m_scriptContext || !m_scriptObject)
958         return;
959
960     ASSERT(resource);
961     ASSERT(resource->scriptObject);
962     if (!resource || !resource->scriptObject)
963         return;
964
965     JSStringRef removeResourceString = JSStringCreateWithUTF8CString("removeResource");
966     JSObjectRef removeResourceFunction = JSValueToObject(m_scriptContext, JSObjectGetProperty(m_scriptContext, m_scriptObject, removeResourceString, 0), 0);
967     JSStringRelease(removeResourceString);
968
969     JSValueRef arguments[] = { resource->scriptObject };
970     JSObjectCallAsFunction(m_scriptContext, removeResourceFunction, m_scriptObject, 1, arguments, 0);
971
972     resource->setScriptObject(0, 0);
973 }
974
975 static void updateResourceRequest(InspectorResource* resource, const ResourceRequest& request)
976 {
977     resource->requestHeaderFields = request.httpHeaderFields();
978     resource->requestURL = request.url();
979 }
980
981 static void updateResourceResponse(InspectorResource* resource, const ResourceResponse& response)
982 {
983     resource->expectedContentLength = response.expectedContentLength();
984     resource->mimeType = response.mimeType();
985     resource->responseHeaderFields = response.httpHeaderFields();
986     resource->responseStatusCode = response.httpStatusCode();
987     resource->suggestedFilename = response.suggestedFilename();
988 }
989
990 void InspectorController::updateScriptResourceRequest(InspectorResource* resource)
991 {
992     ASSERT(resource->scriptObject);
993     ASSERT(m_scriptContext);
994     if (!resource->scriptObject || !m_scriptContext)
995         return;
996
997     String urlString = resource->requestURL.url();
998     JSStringRef url = JSStringCreateWithCharacters(urlString.characters(), urlString.length());
999     JSValueRef urlValue = JSValueMakeString(m_scriptContext, url);
1000     JSStringRelease(url);
1001
1002     urlString = resource->requestURL.host();
1003     JSStringRef domain = JSStringCreateWithCharacters(urlString.characters(), urlString.length());
1004     JSValueRef domainValue = JSValueMakeString(m_scriptContext, domain);
1005     JSStringRelease(domain);
1006
1007     urlString = resource->requestURL.path();
1008     JSStringRef path = JSStringCreateWithCharacters(urlString.characters(), urlString.length());
1009     JSValueRef pathValue = JSValueMakeString(m_scriptContext, path);
1010     JSStringRelease(path);
1011
1012     urlString = resource->requestURL.lastPathComponent();
1013     JSStringRef lastPathComponent = JSStringCreateWithCharacters(urlString.characters(), urlString.length());
1014     JSValueRef lastPathComponentValue = JSValueMakeString(m_scriptContext, lastPathComponent);
1015     JSStringRelease(lastPathComponent);
1016
1017     JSValueRef mainResourceValue = JSValueMakeBoolean(m_scriptContext, m_mainResource == resource);
1018
1019     JSStringRef propertyName = JSStringCreateWithUTF8CString("url");
1020     JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName, urlValue, kJSPropertyAttributeNone, 0);
1021     JSStringRelease(propertyName);
1022
1023     propertyName = JSStringCreateWithUTF8CString("domain");
1024     JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName, domainValue, kJSPropertyAttributeNone, 0);
1025     JSStringRelease(propertyName);
1026
1027     propertyName = JSStringCreateWithUTF8CString("path");
1028     JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName, pathValue, kJSPropertyAttributeNone, 0);
1029     JSStringRelease(propertyName);
1030
1031     propertyName = JSStringCreateWithUTF8CString("lastPathComponent");
1032     JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName, lastPathComponentValue, kJSPropertyAttributeNone, 0);
1033     JSStringRelease(propertyName);
1034
1035     propertyName = JSStringCreateWithUTF8CString("requestHeaders");
1036     JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName, scriptObjectForRequest(m_scriptContext, resource), kJSPropertyAttributeNone, 0);
1037     JSStringRelease(propertyName);
1038
1039     propertyName = JSStringCreateWithUTF8CString("mainResource");
1040     JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName, mainResourceValue, kJSPropertyAttributeNone, 0);
1041     JSStringRelease(propertyName);
1042 }
1043
1044 void InspectorController::updateScriptResourceResponse(InspectorResource* resource)
1045 {
1046     ASSERT(resource->scriptObject);
1047     ASSERT(m_scriptContext);
1048     if (!resource->scriptObject || !m_scriptContext)
1049         return;
1050
1051     JSStringRef mimeType = JSStringCreateWithCharacters(resource->mimeType.characters(), resource->mimeType.length());
1052     JSValueRef mimeTypeValue = JSValueMakeString(m_scriptContext, mimeType);
1053     JSStringRelease(mimeType);
1054
1055     JSStringRef suggestedFilename = JSStringCreateWithCharacters(resource->suggestedFilename.characters(), resource->suggestedFilename.length());
1056     JSValueRef suggestedFilenameValue = JSValueMakeString(m_scriptContext, suggestedFilename);
1057     JSStringRelease(suggestedFilename);
1058
1059     JSValueRef expectedContentLengthValue = JSValueMakeNumber(m_scriptContext, static_cast<double>(resource->expectedContentLength));
1060     JSValueRef statusCodeValue = JSValueMakeNumber(m_scriptContext, resource->responseStatusCode);
1061
1062     JSStringRef propertyName = JSStringCreateWithUTF8CString("mimeType");
1063     JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName, mimeTypeValue, kJSPropertyAttributeNone, 0);
1064     JSStringRelease(propertyName);
1065
1066     propertyName = JSStringCreateWithUTF8CString("suggestedFilename");
1067     JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName, suggestedFilenameValue, kJSPropertyAttributeNone, 0);
1068     JSStringRelease(propertyName);
1069
1070     propertyName = JSStringCreateWithUTF8CString("expectedContentLength");
1071     JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName, expectedContentLengthValue, kJSPropertyAttributeNone, 0);
1072     JSStringRelease(propertyName);
1073
1074     propertyName = JSStringCreateWithUTF8CString("statusCode");
1075     JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName, statusCodeValue, kJSPropertyAttributeNone, 0);
1076     JSStringRelease(propertyName);
1077     
1078     propertyName = JSStringCreateWithUTF8CString("responseHeaders");
1079     JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName, scriptObjectForResponse(m_scriptContext, resource), kJSPropertyAttributeNone, 0);
1080     JSStringRelease(propertyName);
1081
1082     JSValueRef typeValue = JSValueMakeNumber(m_scriptContext, resource->type());
1083     propertyName = JSStringCreateWithUTF8CString("type");
1084     JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName, typeValue, kJSPropertyAttributeNone, 0);
1085     JSStringRelease(propertyName);
1086 }
1087
1088 void InspectorController::updateScriptResource(InspectorResource* resource, int length)
1089 {
1090     ASSERT(resource->scriptObject);
1091     ASSERT(m_scriptContext);
1092     if (!resource->scriptObject || !m_scriptContext)
1093         return;
1094
1095     JSValueRef lengthValue = JSValueMakeNumber(m_scriptContext, length);
1096
1097     JSStringRef propertyName = JSStringCreateWithUTF8CString("contentLength");
1098     JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName, lengthValue, kJSPropertyAttributeNone, 0);
1099     JSStringRelease(propertyName);
1100 }
1101
1102 void InspectorController::updateScriptResource(InspectorResource* resource, bool finished, bool failed)
1103 {
1104     ASSERT(resource->scriptObject);
1105     ASSERT(m_scriptContext);
1106     if (!resource->scriptObject || !m_scriptContext)
1107         return;
1108
1109     JSValueRef failedValue = JSValueMakeBoolean(m_scriptContext, failed);
1110     JSValueRef finishedValue = JSValueMakeBoolean(m_scriptContext, finished);
1111
1112     JSStringRef propertyName = JSStringCreateWithUTF8CString("failed");
1113     JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName, failedValue, kJSPropertyAttributeNone, 0);
1114     JSStringRelease(propertyName);
1115
1116     propertyName = JSStringCreateWithUTF8CString("finished");
1117     JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName, finishedValue, kJSPropertyAttributeNone, 0);
1118     JSStringRelease(propertyName);
1119
1120 }
1121
1122 void InspectorController::updateScriptResource(InspectorResource* resource, double startTime, double responseReceivedTime, double endTime)
1123 {
1124     ASSERT(resource->scriptObject);
1125     ASSERT(m_scriptContext);
1126     if (!resource->scriptObject || !m_scriptContext)
1127         return;
1128
1129     JSValueRef startTimeValue = JSValueMakeNumber(m_scriptContext, startTime);
1130     JSValueRef responseReceivedTimeValue = JSValueMakeNumber(m_scriptContext, responseReceivedTime);
1131     JSValueRef endTimeValue = JSValueMakeNumber(m_scriptContext, endTime);
1132
1133     JSStringRef propertyName = JSStringCreateWithUTF8CString("startTime");
1134     JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName, startTimeValue, kJSPropertyAttributeNone, 0);
1135     JSStringRelease(propertyName);
1136
1137     propertyName = JSStringCreateWithUTF8CString("responseReceivedTime");
1138     JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName, responseReceivedTimeValue, kJSPropertyAttributeNone, 0);
1139     JSStringRelease(propertyName);
1140
1141     propertyName = JSStringCreateWithUTF8CString("endTime");
1142     JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName, endTimeValue, kJSPropertyAttributeNone, 0);
1143     JSStringRelease(propertyName);
1144 }
1145
1146 void InspectorController::populateScriptResources()
1147 {
1148     ASSERT(m_scriptContext);
1149     if (!m_scriptContext)
1150         return;
1151
1152     clearScriptResources();
1153     clearScriptConsoleMessages();
1154     clearDatabaseScriptResources();
1155     clearNetworkTimeline();
1156
1157     ResourcesMap::iterator resourcesEnd = m_resources.end();
1158     for (ResourcesMap::iterator it = m_resources.begin(); it != resourcesEnd; ++it)
1159         addAndUpdateScriptResource(it->second.get());
1160
1161     unsigned messageCount = m_consoleMessages.size();
1162     for (unsigned i = 0; i < messageCount; ++i)
1163         addScriptConsoleMessage(m_consoleMessages[i]);
1164
1165 #if ENABLE(DATABASE)
1166     DatabaseResourcesSet::iterator databasesEnd = m_databaseResources.end();
1167     for (DatabaseResourcesSet::iterator it = m_databaseResources.begin(); it != databasesEnd; ++it)
1168         addDatabaseScriptResource((*it).get());
1169 #endif
1170 }
1171
1172 #if ENABLE(DATABASE)
1173 JSObjectRef InspectorController::addDatabaseScriptResource(InspectorDatabaseResource* resource)
1174 {
1175     ASSERT_ARG(resource, resource);
1176
1177     if (resource->scriptObject)
1178         return resource->scriptObject;
1179
1180     ASSERT(m_scriptContext);
1181     ASSERT(m_scriptObject);
1182     if (!m_scriptContext || !m_scriptObject)
1183         return 0;
1184
1185     JSStringRef databaseString = JSStringCreateWithUTF8CString("Database");
1186     JSObjectRef databaseConstructor = JSValueToObject(m_scriptContext, JSObjectGetProperty(m_scriptContext, m_scriptObject, databaseString, 0), 0);
1187     JSStringRelease(databaseString);
1188
1189     JSValueRef database = toRef(toJS(toJS(m_scriptContext), resource->database.get()));
1190
1191     JSStringRef domain = JSStringCreateWithCharacters(resource->domain.characters(), resource->domain.length());
1192     JSValueRef domainValue = JSValueMakeString(m_scriptContext, domain);
1193     JSStringRelease(domain);
1194
1195     JSStringRef name = JSStringCreateWithCharacters(resource->name.characters(), resource->name.length());
1196     JSValueRef nameValue = JSValueMakeString(m_scriptContext, name);
1197     JSStringRelease(name);
1198
1199     JSStringRef version = JSStringCreateWithCharacters(resource->version.characters(), resource->version.length());
1200     JSValueRef versionValue = JSValueMakeString(m_scriptContext, version);
1201     JSStringRelease(version);
1202
1203     JSValueRef arguments[] = { database, domainValue, nameValue, versionValue };
1204     JSObjectRef result = JSObjectCallAsConstructor(m_scriptContext, databaseConstructor, 4, arguments, 0);
1205
1206     resource->setScriptObject(m_scriptContext, result);
1207
1208     ASSERT(result);
1209
1210     JSStringRef addResourceString = JSStringCreateWithUTF8CString("addResource");
1211     JSObjectRef addResourceFunction = JSValueToObject(m_scriptContext, JSObjectGetProperty(m_scriptContext, m_scriptObject, addResourceString, 0), 0);
1212     JSStringRelease(addResourceString);
1213
1214     JSValueRef addArguments[] = { result };
1215     JSObjectCallAsFunction(m_scriptContext, addResourceFunction, m_scriptObject, 1, addArguments, 0);
1216
1217     return result;
1218 }
1219
1220 void InspectorController::removeDatabaseScriptResource(InspectorDatabaseResource* resource)
1221 {
1222     ASSERT(m_scriptContext);
1223     ASSERT(m_scriptObject);
1224     if (!m_scriptContext || !m_scriptObject)
1225         return;
1226
1227     ASSERT(resource);
1228     ASSERT(resource->scriptObject);
1229     if (!resource || !resource->scriptObject)
1230         return;
1231
1232     JSStringRef removeResourceString = JSStringCreateWithUTF8CString("removeResource");
1233     JSObjectRef removeResourceFunction = JSValueToObject(m_scriptContext, JSObjectGetProperty(m_scriptContext, m_scriptObject, removeResourceString, 0), 0);
1234     JSStringRelease(removeResourceString);
1235
1236     JSValueRef arguments[] = { resource->scriptObject };
1237     JSObjectCallAsFunction(m_scriptContext, removeResourceFunction, m_scriptObject, 1, arguments, 0);
1238
1239     resource->setScriptObject(0, 0);
1240 }
1241 #endif
1242
1243 void InspectorController::addScriptConsoleMessage(const ConsoleMessage* message)
1244 {
1245     ASSERT_ARG(message, message);
1246
1247     JSStringRef messageConstructorString = JSStringCreateWithUTF8CString("ConsoleMessage");
1248     JSObjectRef messageConstructor = JSValueToObject(m_scriptContext, JSObjectGetProperty(m_scriptContext, m_scriptObject, messageConstructorString, 0), 0);
1249     JSStringRelease(messageConstructorString);
1250
1251     JSStringRef addMessageString = JSStringCreateWithUTF8CString("addMessageToConsole");
1252     JSObjectRef addMessage = JSValueToObject(m_scriptContext, JSObjectGetProperty(m_scriptContext, m_scriptObject, addMessageString, 0), 0);
1253     JSStringRelease(addMessageString);
1254
1255     JSValueRef sourceValue = JSValueMakeNumber(m_scriptContext, message->source);
1256     JSValueRef levelValue = JSValueMakeNumber(m_scriptContext, message->level);
1257     JSStringRef messageString = JSStringCreateWithCharacters(message->message.characters(), message->message.length());
1258     JSValueRef messageValue = JSValueMakeString(m_scriptContext, messageString);
1259     JSValueRef lineValue = JSValueMakeNumber(m_scriptContext, message->line);
1260     JSStringRef urlString = JSStringCreateWithCharacters(message->url.characters(), message->url.length());
1261     JSValueRef urlValue = JSValueMakeString(m_scriptContext, urlString);
1262
1263     JSValueRef args[] = { sourceValue, levelValue, messageValue, lineValue, urlValue };
1264     JSObjectRef messageObject = JSObjectCallAsConstructor(m_scriptContext, messageConstructor, 5, args, 0);
1265     JSStringRelease(messageString);
1266     JSStringRelease(urlString);
1267
1268     JSObjectCallAsFunction(m_scriptContext, addMessage, m_scriptObject, 1, &messageObject, 0);
1269 }
1270
1271 void InspectorController::clearScriptResources()
1272 {
1273     if (!m_scriptContext || !m_scriptObject)
1274         return;
1275
1276     ResourcesMap::iterator resourcesEnd = m_resources.end();
1277     for (ResourcesMap::iterator it = m_resources.begin(); it != resourcesEnd; ++it) {
1278         InspectorResource* resource = it->second.get();
1279         resource->setScriptObject(0, 0);
1280     }
1281
1282     callSimpleFunction(m_scriptContext, m_scriptObject, "clearResources");
1283 }
1284
1285 void InspectorController::clearDatabaseScriptResources()
1286 {
1287 #if ENABLE(DATABASE)
1288     if (!m_scriptContext || !m_scriptObject)
1289         return;
1290
1291     DatabaseResourcesSet::iterator databasesEnd = m_databaseResources.end();
1292     for (DatabaseResourcesSet::iterator it = m_databaseResources.begin(); it != databasesEnd; ++it) {
1293         InspectorDatabaseResource* resource = (*it).get();
1294         resource->setScriptObject(0, 0);
1295     }
1296
1297     callSimpleFunction(m_scriptContext, m_scriptObject, "clearDatabaseResources");
1298 #endif
1299 }
1300
1301 void InspectorController::clearScriptConsoleMessages()
1302 {
1303     if (!m_scriptContext || !m_scriptObject)
1304         return;
1305
1306     callSimpleFunction(m_scriptContext, m_scriptObject, "clearConsoleMessages");
1307 }
1308
1309 void InspectorController::clearNetworkTimeline()
1310 {
1311     if (!m_scriptContext || !m_scriptObject)
1312         return;
1313
1314     callSimpleFunction(m_scriptContext, m_scriptObject, "clearNetworkTimeline");
1315 }
1316
1317 void InspectorController::pruneResources(ResourcesMap* resourceMap, DocumentLoader* loaderToKeep)
1318 {
1319     ASSERT_ARG(resourceMap, resourceMap);
1320
1321     ResourcesMap mapCopy(*resourceMap);
1322     ResourcesMap::iterator end = mapCopy.end();
1323     for (ResourcesMap::iterator it = mapCopy.begin(); it != end; ++it) {
1324         InspectorResource* resource = (*it).second.get();
1325         if (resource == m_mainResource)
1326             continue;
1327
1328         if (!loaderToKeep || resource->loader != loaderToKeep) {
1329             removeResource(resource);
1330             if (windowVisible() && resource->scriptObject)
1331                 removeScriptResource(resource);
1332         }
1333     }
1334 }
1335
1336 void InspectorController::didCommitLoad(DocumentLoader* loader)
1337 {
1338     if (!enabled())
1339         return;
1340
1341     if (loader->frame() == m_inspectedPage->mainFrame()) {
1342         ASSERT(m_mainResource);
1343         // FIXME: Should look into asserting that m_mainResource->loader == loader here.
1344
1345         m_client->inspectedURLChanged(loader->URL().url());
1346
1347         deleteAllValues(m_consoleMessages);
1348         m_consoleMessages.clear();
1349
1350 #if ENABLE(DATABASE)
1351         m_databaseResources.clear();
1352 #endif
1353
1354         if (windowVisible()) {
1355             clearScriptConsoleMessages();
1356 #if ENABLE(DATABASE)
1357             clearDatabaseScriptResources();
1358 #endif
1359             clearNetworkTimeline();
1360
1361             // We don't add the main resource until its load is committed. This
1362             // is needed to keep the load for a user-entered URL from showing
1363             // up in the list of resources for the page they are navigating
1364             // away from.
1365             addAndUpdateScriptResource(m_mainResource.get());
1366         }
1367     }
1368
1369     for (Frame* frame = loader->frame(); frame; frame = frame->tree()->traverseNext(loader->frame()))
1370         if (ResourcesMap* resourceMap = m_frameResources.get(frame))
1371             pruneResources(resourceMap, loader);
1372 }
1373
1374 void InspectorController::frameDetachedFromParent(Frame* frame)
1375 {
1376     if (!enabled())
1377         return;
1378     if (ResourcesMap* resourceMap = m_frameResources.get(frame))
1379         removeAllResources(resourceMap);
1380 }
1381
1382 void InspectorController::addResource(InspectorResource* resource)
1383 {
1384     m_resources.set(resource->identifier, resource);
1385
1386     Frame* frame = resource->frame.get();
1387     ResourcesMap* resourceMap = m_frameResources.get(frame);
1388     if (resourceMap)
1389         resourceMap->set(resource->identifier, resource);
1390     else {
1391         resourceMap = new ResourcesMap;
1392         resourceMap->set(resource->identifier, resource);
1393         m_frameResources.set(frame, resourceMap);
1394     }
1395 }
1396
1397 void InspectorController::removeResource(InspectorResource* resource)
1398 {
1399     m_resources.remove(resource->identifier);
1400
1401     Frame* frame = resource->frame.get();
1402     ResourcesMap* resourceMap = m_frameResources.get(frame);
1403     if (!resourceMap) {
1404         ASSERT_NOT_REACHED();
1405         return;
1406     }
1407
1408     resourceMap->remove(resource->identifier);
1409     if (resourceMap->isEmpty()) {
1410         m_frameResources.remove(frame);
1411         delete resourceMap;
1412     }
1413 }
1414
1415 void InspectorController::didLoadResourceFromMemoryCache(DocumentLoader* loader, const ResourceRequest& request, const ResourceResponse& response, int length)
1416 {
1417     if (!enabled())
1418         return;
1419
1420     InspectorResource* resource = new InspectorResource(m_nextIdentifier--, loader, loader->frame());
1421     resource->finished = true;
1422
1423     updateResourceRequest(resource, request);
1424     updateResourceResponse(resource, response);
1425
1426     resource->length = length;
1427     resource->cached = true;
1428     resource->startTime = currentTime();
1429     resource->responseReceivedTime = resource->startTime;
1430     resource->endTime = resource->startTime;
1431
1432     if (loader->frame() == m_inspectedPage->mainFrame() && request.url() == loader->requestURL())
1433         m_mainResource = resource;
1434
1435     addResource(resource);
1436
1437     if (windowVisible())
1438         addAndUpdateScriptResource(resource);
1439 }
1440
1441 void InspectorController::identifierForInitialRequest(unsigned long identifier, DocumentLoader* loader, const ResourceRequest& request)
1442 {
1443     if (!enabled())
1444         return;
1445
1446     InspectorResource* resource = new InspectorResource(identifier, loader, loader->frame());
1447
1448     updateResourceRequest(resource, request);
1449
1450     if (loader->frame() == m_inspectedPage->mainFrame() && request.url() == loader->requestURL())
1451         m_mainResource = resource;
1452
1453     addResource(resource);
1454 }
1455
1456 void InspectorController::willSendRequest(DocumentLoader* loader, unsigned long identifier, ResourceRequest& request, const ResourceResponse& redirectResponse)
1457 {
1458     if (!enabled())
1459         return;
1460
1461     InspectorResource* resource = m_resources.get(identifier).get();
1462     if (!resource)
1463         return;
1464
1465     resource->startTime = currentTime();
1466
1467     if (!redirectResponse.isNull()) {
1468         updateResourceRequest(resource, request);
1469         updateResourceResponse(resource, redirectResponse);
1470     }
1471
1472     if (resource != m_mainResource && windowVisible()) {
1473         if (!resource->scriptObject)
1474             addScriptResource(resource);
1475         else
1476             updateScriptResourceRequest(resource);
1477
1478         updateScriptResource(resource, resource->startTime, resource->responseReceivedTime, resource->endTime);
1479
1480         if (!redirectResponse.isNull())
1481             updateScriptResourceResponse(resource);
1482     }
1483 }
1484
1485 void InspectorController::didReceiveResponse(DocumentLoader*, unsigned long identifier, const ResourceResponse& response)
1486 {
1487     if (!enabled())
1488         return;
1489
1490     InspectorResource* resource = m_resources.get(identifier).get();
1491     if (!resource)
1492         return;
1493
1494     updateResourceResponse(resource, response);
1495
1496     resource->responseReceivedTime = currentTime();
1497
1498     if (windowVisible() && resource->scriptObject) {
1499         updateScriptResourceResponse(resource);
1500         updateScriptResource(resource, resource->startTime, resource->responseReceivedTime, resource->endTime);
1501     }
1502 }
1503
1504 void InspectorController::didReceiveContentLength(DocumentLoader*, unsigned long identifier, int lengthReceived)
1505 {
1506     if (!enabled())
1507         return;
1508
1509     InspectorResource* resource = m_resources.get(identifier).get();
1510     if (!resource)
1511         return;
1512
1513     resource->length += lengthReceived;
1514
1515     if (windowVisible() && resource->scriptObject)
1516         updateScriptResource(resource, resource->length);
1517 }
1518
1519 void InspectorController::didFinishLoading(DocumentLoader* loader, unsigned long identifier)
1520 {
1521     if (!enabled())
1522         return;
1523
1524     RefPtr<InspectorResource> resource = m_resources.get(identifier);
1525     if (!resource)
1526         return;
1527
1528     removeResource(resource.get());
1529
1530     resource->finished = true;
1531     resource->endTime = currentTime();
1532
1533     addResource(resource.get());
1534
1535     if (windowVisible() && resource->scriptObject) {
1536         updateScriptResource(resource.get(), resource->startTime, resource->responseReceivedTime, resource->endTime);
1537         updateScriptResource(resource.get(), resource->finished);
1538     }
1539 }
1540
1541 void InspectorController::didFailLoading(DocumentLoader* loader, unsigned long identifier, const ResourceError& /*error*/)
1542 {
1543     if (!enabled())
1544         return;
1545
1546     RefPtr<InspectorResource> resource = m_resources.get(identifier);
1547     if (!resource)
1548         return;
1549
1550     removeResource(resource.get());
1551
1552     resource->finished = true;
1553     resource->failed = true;
1554     resource->endTime = currentTime();
1555
1556     addResource(resource.get());
1557
1558     if (windowVisible() && resource->scriptObject) {
1559         updateScriptResource(resource.get(), resource->startTime, resource->responseReceivedTime, resource->endTime);
1560         updateScriptResource(resource.get(), resource->finished, resource->failed);
1561     }
1562 }
1563
1564 #if ENABLE(DATABASE)
1565 void InspectorController::didOpenDatabase(Database* database, const String& domain, const String& name, const String& version)
1566 {
1567     if (!enabled())
1568         return;
1569
1570     InspectorDatabaseResource* resource = new InspectorDatabaseResource(database, domain, name, version);
1571
1572     m_databaseResources.add(resource);
1573
1574     if (windowVisible())
1575         addDatabaseScriptResource(resource);
1576 }
1577 #endif
1578
1579 } // namespace WebCore