2010-12-29 Pavel Feldman <pfeldman@chromium.org>
[WebKit-https.git] / WebCore / inspector / InspectorController.cpp
1 /*
2  * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
3  * Copyright (C) 2008 Matt Lilek <webkit@mattlilek.com>
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1.  Redistributions of source code must retain the above copyright
10  *     notice, this list of conditions and the following disclaimer.
11  * 2.  Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution.
14  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15  *     its contributors may be used to endorse or promote products derived
16  *     from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
28  */
29
30 #include "config.h"
31 #include "InspectorController.h"
32
33 #if ENABLE(INSPECTOR)
34
35 #include "CachedResource.h"
36 #include "CachedResourceLoader.h"
37 #include "Chrome.h"
38 #include "Console.h"
39 #include "ConsoleMessage.h"
40 #include "Cookie.h"
41 #include "CookieJar.h"
42 #include "DOMWindow.h"
43 #include "Document.h"
44 #include "DocumentLoader.h"
45 #include "Element.h"
46 #include "FloatConversion.h"
47 #include "FloatQuad.h"
48 #include "FloatRect.h"
49 #include "Frame.h"
50 #include "FrameLoadRequest.h"
51 #include "FrameLoader.h"
52 #include "FrameTree.h"
53 #include "FrameView.h"
54 #include "GraphicsContext.h"
55 #include "HTMLFrameOwnerElement.h"
56 #include "HTTPHeaderMap.h"
57 #include "HitTestResult.h"
58 #include "InjectedScript.h"
59 #include "InjectedScriptHost.h"
60 #include "InspectorBackend.h"
61 #include "InspectorBackendDispatcher.h"
62 #include "InspectorCSSAgent.h"
63 #include "InspectorClient.h"
64 #include "InspectorDOMAgent.h"
65 #include "InspectorDOMStorageResource.h"
66 #include "InspectorDatabaseResource.h"
67 #include "InspectorDebuggerAgent.h"
68 #include "InspectorFrontend.h"
69 #include "InspectorFrontendClient.h"
70 #include "InspectorInstrumentation.h"
71 #include "InspectorProfilerAgent.h"
72 #include "InspectorResourceAgent.h"
73 #include "InspectorState.h"
74 #include "InspectorStorageAgent.h"
75 #include "InspectorTimelineAgent.h"
76 #include "InspectorValues.h"
77 #include "InspectorWorkerResource.h"
78 #include "IntRect.h"
79 #include "Page.h"
80 #include "ProgressTracker.h"
81 #include "Range.h"
82 #include "RenderInline.h"
83 #include "ResourceRequest.h"
84 #include "ResourceResponse.h"
85 #include "ScriptArguments.h"
86 #include "ScriptCallStack.h"
87 #include "ScriptFunctionCall.h"
88 #include "ScriptObject.h"
89 #include "ScriptProfile.h"
90 #include "ScriptProfiler.h"
91 #include "ScriptSourceCode.h"
92 #include "ScriptState.h"
93 #include "SecurityOrigin.h"
94 #include "Settings.h"
95 #include "SharedBuffer.h"
96 #include "TextEncoding.h"
97 #include "TextIterator.h"
98 #include "UserGestureIndicator.h"
99 #include "WindowFeatures.h"
100 #include <wtf/text/StringConcatenate.h>
101 #include <wtf/CurrentTime.h>
102 #include <wtf/ListHashSet.h>
103 #include <wtf/RefCounted.h>
104 #include <wtf/StdLibExtras.h>
105 #include <wtf/UnusedParam.h>
106
107 #if ENABLE(DATABASE)
108 #include "Database.h"
109 #endif
110
111 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
112 #include "InspectorApplicationCacheAgent.h"
113 #endif
114
115 #if ENABLE(FILE_SYSTEM)
116 #include "InspectorFileSystemAgent.h"
117 #endif
118
119 #if ENABLE(DOM_STORAGE)
120 #include "Storage.h"
121 #include "StorageArea.h"
122 #endif
123
124 using namespace std;
125
126 namespace WebCore {
127
128 const char* const InspectorController::ElementsPanel = "elements";
129 const char* const InspectorController::ConsolePanel = "console";
130 const char* const InspectorController::ScriptsPanel = "scripts";
131 const char* const InspectorController::ProfilesPanel = "profiles";
132
133 const unsigned InspectorController::defaultAttachedHeight = 300;
134
135 static const unsigned maximumConsoleMessages = 1000;
136 static const unsigned expireConsoleMessagesStep = 100;
137
138 InspectorController::InspectorController(Page* page, InspectorClient* client)
139     : m_inspectedPage(page)
140     , m_client(client)
141     , m_openingFrontend(false)
142     , m_cssAgent(new InspectorCSSAgent())
143     , m_mainResourceIdentifier(0)
144     , m_expiredConsoleMessageCount(0)
145     , m_previousMessage(0)
146     , m_settingsLoaded(false)
147     , m_inspectorBackend(InspectorBackend::create(this))
148     , m_inspectorBackendDispatcher(new InspectorBackendDispatcher(this))
149     , m_injectedScriptHost(InjectedScriptHost::create(this))
150 #if ENABLE(JAVASCRIPT_DEBUGGER)
151     , m_attachDebuggerWhenShown(false)
152     , m_hasXHRBreakpointWithEmptyURL(false)
153     , m_profilerAgent(InspectorProfilerAgent::create(this))
154 #endif
155 {
156     m_state = new InspectorState(client);
157     ASSERT_ARG(page, page);
158     ASSERT_ARG(client, client);
159 }
160
161 InspectorController::~InspectorController()
162 {
163     // These should have been cleared in inspectedPageDestroyed().
164     ASSERT(!m_client);
165     ASSERT(!m_inspectedPage);
166     ASSERT(!m_highlightedNode);
167
168     releaseFrontendLifetimeAgents();
169
170     m_inspectorBackend->disconnectController();
171     m_injectedScriptHost->disconnectController();
172 }
173
174 void InspectorController::inspectedPageDestroyed()
175 {
176     if (m_frontend)
177         m_frontend->disconnectFromBackend();
178
179     hideHighlight();
180
181 #if ENABLE(JAVASCRIPT_DEBUGGER)
182     m_debuggerAgent.clear();
183 #endif
184     ASSERT(m_inspectedPage);
185     m_inspectedPage = 0;
186
187     m_client->inspectorDestroyed();
188     m_client = 0;
189 }
190
191 bool InspectorController::enabled() const
192 {
193     if (!m_inspectedPage)
194         return false;
195     return m_inspectedPage->settings()->developerExtrasEnabled();
196 }
197
198 bool InspectorController::inspectorStartsAttached()
199 {
200     return m_state->getBoolean(InspectorState::inspectorStartsAttached);
201 }
202
203 void InspectorController::setInspectorStartsAttached(bool attached)
204 {
205     m_state->setBoolean(InspectorState::inspectorStartsAttached, attached);
206 }
207
208 void InspectorController::setInspectorAttachedHeight(long height)
209 {
210     m_state->setLong(InspectorState::inspectorAttachedHeight, height);
211 }
212
213 int InspectorController::inspectorAttachedHeight() const
214 {
215     return m_state->getBoolean(InspectorState::inspectorAttachedHeight);
216 }
217
218 bool InspectorController::searchingForNodeInPage() const
219 {
220     return m_state->getBoolean(InspectorState::searchingForNode);
221 }
222
223 void InspectorController::getInspectorState(RefPtr<InspectorObject>* state)
224 {
225 #if ENABLE(JAVASCRIPT_DEBUGGER)
226     if (m_debuggerAgent)
227         m_state->setLong(InspectorState::pauseOnExceptionsState, m_debuggerAgent->pauseOnExceptionsState());
228 #endif
229     *state = m_state->generateStateObjectForFrontend();
230 }
231
232 void InspectorController::restoreInspectorStateFromCookie(const String& inspectorStateCookie)
233 {
234     m_state->restoreFromInspectorCookie(inspectorStateCookie);
235     if (m_state->getBoolean(InspectorState::timelineProfilerEnabled))
236         startTimelineProfiler();
237 #if ENABLE(JAVASCRIPT_DEBUGGER)
238     if (m_state->getBoolean(InspectorState::userInitiatedProfiling))
239         startUserInitiatedProfiling();
240 #endif    
241 }
242
243 void InspectorController::inspect(Node* node)
244 {
245     if (!enabled())
246         return;
247
248     show();
249
250     if (node->nodeType() != Node::ELEMENT_NODE && node->nodeType() != Node::DOCUMENT_NODE)
251         node = node->parentNode();
252     m_nodeToFocus = node;
253
254     if (!m_frontend)
255         return;
256
257     focusNode();
258 }
259
260 void InspectorController::focusNode()
261 {
262     if (!enabled())
263         return;
264
265     ASSERT(m_frontend);
266     ASSERT(m_nodeToFocus);
267
268     long id = m_domAgent->pushNodePathToFrontend(m_nodeToFocus.get());
269     m_frontend->updateFocusedNode(id);
270     m_nodeToFocus = 0;
271 }
272
273 void InspectorController::highlight(Node* node)
274 {
275     if (!enabled())
276         return;
277     ASSERT_ARG(node, node);
278     m_highlightedNode = node;
279     m_client->highlight(node);
280 }
281
282 void InspectorController::highlightDOMNode(long nodeId)
283 {
284     Node* node = 0;
285     if (m_domAgent && (node = m_domAgent->nodeForId(nodeId)))
286         highlight(node);
287 }
288
289 void InspectorController::highlightFrame(unsigned long frameId)
290 {
291     Frame* mainFrame = m_inspectedPage->mainFrame();
292     for (Frame* frame = mainFrame; frame; frame = frame->tree()->traverseNext(mainFrame)) {
293         if (reinterpret_cast<uintptr_t>(frame) == frameId && frame->ownerElement()) {
294             highlight(frame->ownerElement());
295             return;
296         }
297     }
298 }
299
300 void InspectorController::hideHighlight()
301 {
302     if (!enabled())
303         return;
304     m_highlightedNode = 0;
305     m_client->hideHighlight();
306 }
307
308 void InspectorController::setConsoleMessagesEnabled(bool enabled, bool* newState)
309 {
310     *newState = enabled;
311     setConsoleMessagesEnabled(enabled);
312 }
313
314 void InspectorController::setConsoleMessagesEnabled(bool enabled)
315 {
316     m_state->setBoolean(InspectorState::consoleMessagesEnabled, enabled);
317     if (!enabled)
318         return;
319
320     if (m_expiredConsoleMessageCount)
321         m_frontend->updateConsoleMessageExpiredCount(m_expiredConsoleMessageCount);
322     unsigned messageCount = m_consoleMessages.size();
323     for (unsigned i = 0; i < messageCount; ++i)
324         m_consoleMessages[i]->addToFrontend(m_frontend.get(), m_injectedScriptHost.get());
325 }
326
327 void InspectorController::addMessageToConsole(MessageSource source, MessageType type, MessageLevel level, const String& message, PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack)
328 {
329     if (!enabled())
330         return;
331
332     addConsoleMessage(new ConsoleMessage(source, type, level, message, arguments, callStack));
333 }
334
335 void InspectorController::addMessageToConsole(MessageSource source, MessageType type, MessageLevel level, const String& message, unsigned lineNumber, const String& sourceID)
336 {
337     if (!enabled())
338         return;
339
340     addConsoleMessage(new ConsoleMessage(source, type, level, message, lineNumber, sourceID));
341 }
342
343 void InspectorController::addConsoleMessage(PassOwnPtr<ConsoleMessage> consoleMessage)
344 {
345     ASSERT(enabled());
346     ASSERT_ARG(consoleMessage, consoleMessage);
347
348     if (m_previousMessage && m_previousMessage->isEqual(consoleMessage.get())) {
349         m_previousMessage->incrementCount();
350         if (m_state->getBoolean(InspectorState::consoleMessagesEnabled) && m_frontend)
351             m_previousMessage->updateRepeatCountInConsole(m_frontend.get());
352     } else {
353         m_previousMessage = consoleMessage.get();
354         m_consoleMessages.append(consoleMessage);
355         if (m_state->getBoolean(InspectorState::consoleMessagesEnabled) && m_frontend)
356             m_previousMessage->addToFrontend(m_frontend.get(), m_injectedScriptHost.get());
357     }
358
359     if (!m_frontend && m_consoleMessages.size() >= maximumConsoleMessages) {
360         m_expiredConsoleMessageCount += expireConsoleMessagesStep;
361         m_consoleMessages.remove(0, expireConsoleMessagesStep);
362     }
363 }
364
365 void InspectorController::clearConsoleMessages()
366 {
367     m_consoleMessages.clear();
368     m_expiredConsoleMessageCount = 0;
369     m_previousMessage = 0;
370     m_injectedScriptHost->releaseWrapperObjectGroup(0 /* release the group in all scripts */, "console");
371     if (m_domAgent)
372         m_domAgent->releaseDanglingNodes();
373     if (m_frontend)
374         m_frontend->consoleMessagesCleared();
375 }
376
377 void InspectorController::startGroup(PassRefPtr<ScriptArguments> arguments, PassRefPtr<ScriptCallStack> callStack, bool collapsed)
378 {
379     addConsoleMessage(new ConsoleMessage(JSMessageSource, collapsed ? StartGroupCollapsedMessageType : StartGroupMessageType, LogMessageLevel, "", arguments, callStack));
380 }
381
382 void InspectorController::endGroup(MessageSource source, unsigned lineNumber, const String& sourceURL)
383 {
384     addConsoleMessage(new ConsoleMessage(source, EndGroupMessageType, LogMessageLevel, String(), lineNumber, sourceURL));
385 }
386
387 void InspectorController::markTimeline(const String& message)
388 {
389     if (timelineAgent())
390         timelineAgent()->didMarkTimeline(message);
391 }
392
393 void InspectorController::mouseDidMoveOverElement(const HitTestResult& result, unsigned)
394 {
395     if (!enabled() || !searchingForNodeInPage())
396         return;
397
398     Node* node = result.innerNode();
399     while (node && node->nodeType() == Node::TEXT_NODE)
400         node = node->parentNode();
401     if (node)
402         highlight(node);
403 }
404
405 void InspectorController::handleMousePress()
406 {
407     if (!enabled())
408         return;
409
410     ASSERT(searchingForNodeInPage());
411     if (!m_highlightedNode)
412         return;
413
414     RefPtr<Node> node = m_highlightedNode;
415     setSearchingForNode(false);
416     inspect(node.get());
417 }
418
419 void InspectorController::setInspectorFrontendClient(PassOwnPtr<InspectorFrontendClient> client)
420 {
421     ASSERT(!m_inspectorFrontendClient);
422     m_inspectorFrontendClient = client;
423 }
424
425 void InspectorController::inspectedWindowScriptObjectCleared(Frame* frame)
426 {
427     // If the page is supposed to serve as InspectorFrontend notify inspetor frontend
428     // client that it's cleared so that the client can expose inspector bindings.
429     if (m_inspectorFrontendClient && frame == m_inspectedPage->mainFrame())
430         m_inspectorFrontendClient->windowObjectCleared();
431
432     if (enabled()) {
433         if (m_frontend && frame == m_inspectedPage->mainFrame())
434             m_injectedScriptHost->discardInjectedScripts();
435         if (m_scriptsToEvaluateOnLoad.size()) {
436             ScriptState* scriptState = mainWorldScriptState(frame);
437             for (Vector<String>::iterator it = m_scriptsToEvaluateOnLoad.begin();
438                  it != m_scriptsToEvaluateOnLoad.end(); ++it) {
439                 m_injectedScriptHost->injectScript(*it, scriptState);
440             }
441         }
442     }
443     if (!m_inspectorExtensionAPI.isEmpty())
444         m_injectedScriptHost->injectScript(m_inspectorExtensionAPI, mainWorldScriptState(frame));
445 }
446
447 void InspectorController::setSearchingForNode(bool enabled)
448 {
449     if (searchingForNodeInPage() == enabled)
450         return;
451     m_state->setBoolean(InspectorState::searchingForNode, enabled);
452     if (!enabled)
453         hideHighlight();
454 }
455
456 void InspectorController::setSearchingForNode(bool enabled, bool* newState)
457 {
458     *newState = enabled;
459     setSearchingForNode(enabled);
460 }
461
462 void InspectorController::setMonitoringXHREnabled(bool enabled, bool* newState)
463 {
464     *newState = enabled;
465     m_state->setBoolean(InspectorState::monitoringXHR, enabled);
466 }
467
468 void InspectorController::connectFrontend()
469 {
470     m_openingFrontend = false;
471     releaseFrontendLifetimeAgents();
472     m_frontend = new InspectorFrontend(m_client);
473     m_domAgent = InspectorDOMAgent::create(m_frontend.get());
474     m_resourceAgent = InspectorResourceAgent::create(m_inspectedPage, m_frontend.get());
475
476     m_cssAgent->setDOMAgent(m_domAgent.get());
477
478 #if ENABLE(DATABASE)
479     m_storageAgent = InspectorStorageAgent::create(m_frontend.get());
480 #endif
481
482     if (m_timelineAgent)
483         m_timelineAgent->resetFrontendProxyObject(m_frontend.get());
484
485     // Initialize Web Inspector title.
486     m_frontend->inspectedURLChanged(m_inspectedPage->mainFrame()->loader()->url().string());
487
488 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
489     m_applicationCacheAgent = new InspectorApplicationCacheAgent(this, m_frontend.get());
490 #endif
491
492 #if ENABLE(FILE_SYSTEM)
493     m_fileSystemAgent = InspectorFileSystemAgent::create(this, m_frontend.get());
494 #endif
495     
496     if (!InspectorInstrumentation::hasFrontends())
497         ScriptController::setCaptureCallStackForUncaughtExceptions(true);
498     InspectorInstrumentation::frontendCreated();
499 }
500
501 void InspectorController::reuseFrontend()
502 {
503     connectFrontend();
504     restoreDebugger();
505     restoreProfiler(ProfilerRestoreResetAgent);
506 }
507
508 void InspectorController::show()
509 {
510     if (!enabled())
511         return;
512
513     if (m_openingFrontend)
514         return;
515
516     if (m_frontend)
517         m_frontend->bringToFront();
518     else {
519         m_openingFrontend = true;
520         m_client->openInspectorFrontend(this);
521     }
522 }
523
524 void InspectorController::showPanel(const String& panel)
525 {
526     if (!enabled())
527         return;
528
529     show();
530
531     if (!m_frontend) {
532         m_showAfterVisible = panel;
533         return;
534     }
535     m_frontend->showPanel(panel);
536 }
537
538 void InspectorController::close()
539 {
540     if (!m_frontend)
541         return;
542     m_frontend->disconnectFromBackend();
543     disconnectFrontend();
544 }
545
546 void InspectorController::disconnectFrontend()
547 {
548     if (!m_frontend)
549         return;
550
551     m_frontend.clear();
552
553     InspectorInstrumentation::frontendDeleted();
554     if (!InspectorInstrumentation::hasFrontends())
555         ScriptController::setCaptureCallStackForUncaughtExceptions(false);
556
557 #if ENABLE(JAVASCRIPT_DEBUGGER)
558     // If the window is being closed with the debugger enabled,
559     // remember this state to re-enable debugger on the next window
560     // opening.
561     bool debuggerWasEnabled = debuggerEnabled();
562     disableDebugger();
563     m_attachDebuggerWhenShown = debuggerWasEnabled;
564 #endif
565     setSearchingForNode(false);
566     unbindAllResources();
567     stopTimelineProfiler();
568
569     hideHighlight();
570
571 #if ENABLE(JAVASCRIPT_DEBUGGER)
572     m_profilerAgent->setFrontend(0);
573     m_profilerAgent->stopUserInitiatedProfiling(true);
574 #endif
575
576     releaseFrontendLifetimeAgents();
577     m_timelineAgent.clear();
578     m_extraHeaders.clear();
579 }
580
581 void InspectorController::releaseFrontendLifetimeAgents()
582 {
583     m_resourceAgent.clear();
584
585     // This should be invoked prior to m_domAgent destruction.
586     m_cssAgent->setDOMAgent(0);
587
588     // m_domAgent is RefPtr. Remove DOM listeners first to ensure that there are
589     // no references to the DOM agent from the DOM tree.
590     if (m_domAgent)
591         m_domAgent->reset();
592     m_domAgent.clear();
593
594 #if ENABLE(DATABASE)
595     if (m_storageAgent)
596         m_storageAgent->clearFrontend();
597     m_storageAgent.clear();
598 #endif
599
600 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
601     m_applicationCacheAgent.clear();
602 #endif
603
604 #if ENABLE(FILE_SYSTEM)
605     if (m_fileSystemAgent)
606         m_fileSystemAgent->stop(); 
607         m_fileSystemAgent.clear();
608 #endif
609 }
610
611 void InspectorController::populateScriptObjects()
612 {
613     ASSERT(m_frontend);
614     if (!m_frontend)
615         return;
616
617     if (!m_showAfterVisible.isEmpty()) {
618         showPanel(m_showAfterVisible);
619         m_showAfterVisible = "";
620     }
621
622 #if ENABLE(JAVASCRIPT_DEBUGGER)
623     if (m_profilerAgent->enabled())
624         m_frontend->profilerWasEnabled();
625 #endif
626
627     m_domAgent->setDocument(m_inspectedPage->mainFrame()->document());
628
629     if (m_nodeToFocus)
630         focusNode();
631
632 #if ENABLE(DATABASE)
633     DatabaseResourcesMap::iterator databasesEnd = m_databaseResources.end();
634     for (DatabaseResourcesMap::iterator it = m_databaseResources.begin(); it != databasesEnd; ++it)
635         it->second->bind(m_frontend.get());
636 #endif
637 #if ENABLE(DOM_STORAGE)
638     DOMStorageResourcesMap::iterator domStorageEnd = m_domStorageResources.end();
639     for (DOMStorageResourcesMap::iterator it = m_domStorageResources.begin(); it != domStorageEnd; ++it)
640         it->second->bind(m_frontend.get());
641 #endif
642 #if ENABLE(JAVASCRIPT_DEBUGGER) && ENABLE(WORKERS)
643     WorkersMap::iterator workersEnd = m_workers.end();
644     for (WorkersMap::iterator it = m_workers.begin(); it != workersEnd; ++it) {
645         InspectorWorkerResource* worker = it->second.get();
646         m_frontend->didCreateWorker(worker->id(), worker->url(), worker->isSharedWorker());
647     }
648 #endif
649
650     // Dispatch pending frontend commands
651     for (Vector<pair<long, String> >::iterator it = m_pendingEvaluateTestCommands.begin(); it != m_pendingEvaluateTestCommands.end(); ++it)
652         m_frontend->evaluateForTestInFrontend((*it).first, (*it).second);
653     m_pendingEvaluateTestCommands.clear();
654
655     restoreDebugger();
656     restoreProfiler(ProfilerRestoreNoAction);
657 }
658
659 void InspectorController::restoreDebugger()
660 {
661     ASSERT(m_frontend);
662 #if ENABLE(JAVASCRIPT_DEBUGGER)
663     if (InspectorDebuggerAgent::isDebuggerAlwaysEnabled())
664         enableDebuggerFromFrontend(false);
665     else {
666         if (m_state->getBoolean(InspectorState::debuggerAlwaysEnabled) || m_attachDebuggerWhenShown)
667             enableDebugger();
668     }
669 #endif
670 }
671
672 void InspectorController::restoreProfiler(ProfilerRestoreAction action)
673 {
674     ASSERT(m_frontend);
675 #if ENABLE(JAVASCRIPT_DEBUGGER)
676     m_profilerAgent->setFrontend(m_frontend.get());
677     if (!ScriptProfiler::isProfilerAlwaysEnabled() && m_state->getBoolean(InspectorState::profilerAlwaysEnabled))
678         enableProfiler();
679     if (action == ProfilerRestoreResetAgent)
680         m_profilerAgent->resetState();
681 #endif
682 }
683
684 void InspectorController::unbindAllResources()
685 {
686 #if ENABLE(DATABASE)
687     DatabaseResourcesMap::iterator databasesEnd = m_databaseResources.end();
688     for (DatabaseResourcesMap::iterator it = m_databaseResources.begin(); it != databasesEnd; ++it)
689         it->second->unbind();
690 #endif
691 #if ENABLE(DOM_STORAGE)
692     DOMStorageResourcesMap::iterator domStorageEnd = m_domStorageResources.end();
693     for (DOMStorageResourcesMap::iterator it = m_domStorageResources.begin(); it != domStorageEnd; ++it)
694         it->second->unbind();
695 #endif
696     if (m_timelineAgent)
697         m_timelineAgent->reset();
698 }
699
700 void InspectorController::didCommitLoad(DocumentLoader* loader)
701 {
702     if (!enabled())
703         return;
704
705     if (m_resourceAgent)
706         m_resourceAgent->didCommitLoad(loader);
707     
708     ASSERT(m_inspectedPage);
709
710     if (loader->frame() == m_inspectedPage->mainFrame()) {
711         if (m_frontend)
712             m_frontend->inspectedURLChanged(loader->url().string());
713
714         m_injectedScriptHost->discardInjectedScripts();
715         clearConsoleMessages();
716
717         m_times.clear();
718         m_counts.clear();
719
720 #if ENABLE(JAVASCRIPT_DEBUGGER)
721         if (m_debuggerAgent) {
722             m_debuggerAgent->clearForPageNavigation();
723             restoreStickyBreakpoints();
724         }
725 #endif
726
727 #if ENABLE(JAVASCRIPT_DEBUGGER) && USE(JSC)
728         m_profilerAgent->stopUserInitiatedProfiling(true);
729         m_profilerAgent->resetState();
730 #endif
731
732         // unbindAllResources should be called before database and DOM storage
733         // resources are cleared so that it has a chance to unbind them.
734         unbindAllResources();
735
736         if (m_frontend) {
737             m_frontend->reset();
738             m_domAgent->reset();
739             m_cssAgent->reset();
740         }
741 #if ENABLE(WORKERS)
742         m_workers.clear();
743 #endif
744 #if ENABLE(DATABASE)
745         m_databaseResources.clear();
746 #endif
747 #if ENABLE(DOM_STORAGE)
748         m_domStorageResources.clear();
749 #endif
750
751         if (m_frontend) {
752             m_mainResourceIdentifier = 0;
753             m_frontend->didCommitLoad();
754             m_domAgent->setDocument(m_inspectedPage->mainFrame()->document());
755         }
756     }
757 }
758
759 void InspectorController::frameDetachedFromParent(Frame* rootFrame)
760 {
761     if (!enabled())
762         return;
763
764     if (m_resourceAgent)
765         m_resourceAgent->frameDetachedFromParent(rootFrame);
766 }
767
768 void InspectorController::didLoadResourceFromMemoryCache(DocumentLoader* loader, const CachedResource* cachedResource)
769 {
770     if (!enabled())
771         return;
772
773     ensureSettingsLoaded();
774
775     if (m_resourceAgent)
776         m_resourceAgent->didLoadResourceFromMemoryCache(loader, cachedResource);
777 }
778
779 void InspectorController::identifierForInitialRequest(unsigned long identifier, DocumentLoader* loader, const ResourceRequest& request)
780 {
781     if (!enabled())
782         return;
783     ASSERT(m_inspectedPage);
784
785     bool isMainResource = isMainResourceLoader(loader, request.url());
786     if (isMainResource)
787         m_mainResourceIdentifier = identifier;
788
789     ensureSettingsLoaded();
790
791     if (m_resourceAgent)
792         m_resourceAgent->identifierForInitialRequest(identifier, request.url(), loader);
793 }
794
795 void InspectorController::mainResourceFiredDOMContentEvent(DocumentLoader* loader, const KURL& url)
796 {
797     if (!enabled() || !isMainResourceLoader(loader, url))
798         return;
799
800     if (m_timelineAgent)
801         m_timelineAgent->didMarkDOMContentEvent();
802     if (m_frontend)
803         m_frontend->domContentEventFired(currentTime());
804 }
805
806 void InspectorController::mainResourceFiredLoadEvent(DocumentLoader* loader, const KURL& url)
807 {
808     if (!enabled() || !isMainResourceLoader(loader, url))
809         return;
810
811     if (m_timelineAgent)
812         m_timelineAgent->didMarkLoadEvent();
813     if (m_frontend)
814         m_frontend->loadEventFired(currentTime());
815 }
816
817 bool InspectorController::isMainResourceLoader(DocumentLoader* loader, const KURL& requestUrl)
818 {
819     return loader->frame() == m_inspectedPage->mainFrame() && requestUrl == loader->requestURL();
820 }
821
822 void InspectorController::willSendRequest(unsigned long identifier, ResourceRequest& request, const ResourceResponse& redirectResponse)
823 {
824     if (!enabled())
825         return;
826
827     request.setReportLoadTiming(true);
828
829     if (m_frontend) {
830         // Only enable raw headers if front-end is attached, as otherwise we may lack
831         // permissions to fetch the headers.
832         request.setReportRawHeaders(true);
833
834         if (m_extraHeaders) {
835             HTTPHeaderMap::const_iterator end = m_extraHeaders->end();
836             for (HTTPHeaderMap::const_iterator it = m_extraHeaders->begin(); it != end; ++it)
837                 request.setHTTPHeaderField(it->first, it->second);
838         }
839     }
840
841     bool isMainResource = m_mainResourceIdentifier == identifier;
842     if (m_timelineAgent)
843         m_timelineAgent->willSendResourceRequest(identifier, isMainResource, request);
844
845     if (m_resourceAgent)
846         m_resourceAgent->willSendRequest(identifier, request, redirectResponse);
847 }
848
849 void InspectorController::markResourceAsCached(unsigned long identifier)
850 {
851     if (!enabled())
852         return;
853
854     if (m_resourceAgent)
855         m_resourceAgent->markResourceAsCached(identifier);
856 }
857
858 void InspectorController::didReceiveResponse(unsigned long identifier, DocumentLoader* loader, const ResourceResponse& response)
859 {
860     if (!enabled())
861         return;
862
863     if (m_resourceAgent)
864         m_resourceAgent->didReceiveResponse(identifier, loader, response);
865
866     if (response.httpStatusCode() >= 400) {
867         String message = makeString("Failed to load resource: the server responded with a status of ", String::number(response.httpStatusCode()), " (", response.httpStatusText(), ')');
868         addConsoleMessage(new ConsoleMessage(OtherMessageSource, NetworkErrorMessageType, ErrorMessageLevel, message, response.url().string(), identifier));
869     }
870 }
871
872 void InspectorController::didReceiveContentLength(unsigned long identifier, int lengthReceived)
873 {
874     if (!enabled())
875         return;
876
877     if (m_resourceAgent)
878         m_resourceAgent->didReceiveContentLength(identifier, lengthReceived);
879 }
880
881 void InspectorController::didFinishLoading(unsigned long identifier, double finishTime)
882 {
883     if (!enabled())
884         return;
885
886     if (m_timelineAgent)
887         m_timelineAgent->didFinishLoadingResource(identifier, false, finishTime);
888
889     if (m_resourceAgent)
890         m_resourceAgent->didFinishLoading(identifier, finishTime);
891 }
892
893 void InspectorController::didFailLoading(unsigned long identifier, const ResourceError& error)
894 {
895     if (!enabled())
896         return;
897
898     if (m_timelineAgent)
899         m_timelineAgent->didFinishLoadingResource(identifier, true, 0);
900
901     String message = "Failed to load resource";
902         if (!error.localizedDescription().isEmpty())
903             message += ": " + error.localizedDescription();
904         addConsoleMessage(new ConsoleMessage(OtherMessageSource, NetworkErrorMessageType, ErrorMessageLevel, message, error.failingURL(), identifier));
905
906     if (m_resourceAgent)
907         m_resourceAgent->didFailLoading(identifier, error);
908 }
909
910 void InspectorController::resourceRetrievedByXMLHttpRequest(unsigned long identifier, const String& sourceString, const String& url, const String& sendURL, unsigned sendLineNumber)
911 {
912     if (!enabled())
913         return;
914
915     if (m_state->getBoolean(InspectorState::monitoringXHR))
916         addMessageToConsole(JSMessageSource, LogMessageType, LogMessageLevel, "XHR finished loading: \"" + url + "\".", sendLineNumber, sendURL);
917
918     if (m_resourceAgent)
919         m_resourceAgent->setInitialContent(identifier, sourceString, "XHR");
920 }
921
922 void InspectorController::scriptImported(unsigned long identifier, const String& sourceString)
923 {
924     if (!enabled())
925         return;
926
927     if (m_resourceAgent)
928         m_resourceAgent->setInitialContent(identifier, sourceString, "Script");
929 }
930
931 void InspectorController::ensureSettingsLoaded()
932 {
933     if (m_settingsLoaded)
934         return;
935     m_settingsLoaded = true;
936
937     m_state->loadFromSettings();
938 }
939
940 void InspectorController::startTimelineProfiler()
941 {
942     if (!enabled())
943         return;
944
945     if (m_timelineAgent)
946         return;
947
948     m_timelineAgent = new InspectorTimelineAgent(m_frontend.get());
949     if (m_frontend)
950         m_frontend->timelineProfilerWasStarted();
951
952     m_state->setBoolean(InspectorState::timelineProfilerEnabled, true);
953 }
954
955 void InspectorController::stopTimelineProfiler()
956 {
957     if (!enabled())
958         return;
959
960     if (!m_timelineAgent)
961         return;
962
963     m_timelineAgent = 0;
964     if (m_frontend)
965         m_frontend->timelineProfilerWasStopped();
966
967     m_state->setBoolean(InspectorState::timelineProfilerEnabled, false);
968 }
969
970 #if ENABLE(WORKERS)
971 class PostWorkerNotificationToFrontendTask : public ScriptExecutionContext::Task {
972 public:
973     static PassOwnPtr<PostWorkerNotificationToFrontendTask> create(PassRefPtr<InspectorWorkerResource> worker, InspectorController::WorkerAction action)
974     {
975         return new PostWorkerNotificationToFrontendTask(worker, action);
976     }
977
978 private:
979     PostWorkerNotificationToFrontendTask(PassRefPtr<InspectorWorkerResource> worker, InspectorController::WorkerAction action)
980         : m_worker(worker)
981         , m_action(action)
982     {
983     }
984
985     virtual void performTask(ScriptExecutionContext* scriptContext)
986     {
987         if (InspectorController* inspector = scriptContext->inspectorController())
988             inspector->postWorkerNotificationToFrontend(*m_worker, m_action);
989     }
990
991 private:
992     RefPtr<InspectorWorkerResource> m_worker;
993     InspectorController::WorkerAction m_action;
994 };
995
996 void InspectorController::postWorkerNotificationToFrontend(const InspectorWorkerResource& worker, InspectorController::WorkerAction action)
997 {
998     if (!m_frontend)
999         return;
1000 #if ENABLE(JAVASCRIPT_DEBUGGER)
1001     switch (action) {
1002     case InspectorController::WorkerCreated:
1003         m_frontend->didCreateWorker(worker.id(), worker.url(), worker.isSharedWorker());
1004         break;
1005     case InspectorController::WorkerDestroyed:
1006         m_frontend->didDestroyWorker(worker.id());
1007         break;
1008     }
1009 #endif
1010 }
1011
1012 void InspectorController::didCreateWorker(intptr_t id, const String& url, bool isSharedWorker)
1013 {
1014     if (!enabled())
1015         return;
1016
1017     RefPtr<InspectorWorkerResource> workerResource(InspectorWorkerResource::create(id, url, isSharedWorker));
1018     m_workers.set(id, workerResource);
1019     if (m_inspectedPage && m_frontend)
1020         m_inspectedPage->mainFrame()->document()->postTask(PostWorkerNotificationToFrontendTask::create(workerResource, InspectorController::WorkerCreated));
1021 }
1022
1023 void InspectorController::didDestroyWorker(intptr_t id)
1024 {
1025     if (!enabled())
1026         return;
1027
1028     WorkersMap::iterator workerResource = m_workers.find(id);
1029     if (workerResource == m_workers.end())
1030         return;
1031     if (m_inspectedPage && m_frontend)
1032         m_inspectedPage->mainFrame()->document()->postTask(PostWorkerNotificationToFrontendTask::create(workerResource->second, InspectorController::WorkerDestroyed));
1033     m_workers.remove(workerResource);
1034 }
1035 #endif // ENABLE(WORKERS)
1036
1037 #if ENABLE(DATABASE)
1038 void InspectorController::selectDatabase(Database* database)
1039 {
1040     if (!m_frontend)
1041         return;
1042
1043     for (DatabaseResourcesMap::iterator it = m_databaseResources.begin(); it != m_databaseResources.end(); ++it) {
1044         if (it->second->database() == database) {
1045             m_frontend->selectDatabase(it->first);
1046             break;
1047         }
1048     }
1049 }
1050
1051 Database* InspectorController::databaseForId(long databaseId)
1052 {
1053     DatabaseResourcesMap::iterator it = m_databaseResources.find(databaseId);
1054     if (it == m_databaseResources.end())
1055         return 0;
1056     return it->second->database();
1057 }
1058
1059 void InspectorController::didOpenDatabase(PassRefPtr<Database> database, const String& domain, const String& name, const String& version)
1060 {
1061     if (!enabled())
1062         return;
1063
1064     RefPtr<InspectorDatabaseResource> resource = InspectorDatabaseResource::create(database, domain, name, version);
1065
1066     m_databaseResources.set(resource->id(), resource);
1067
1068     // Resources are only bound while visible.
1069     if (m_frontend)
1070         resource->bind(m_frontend.get());
1071 }
1072 #endif
1073
1074 void InspectorController::getCookies(RefPtr<InspectorArray>* cookies, WTF::String* cookiesString)
1075 {
1076     // If we can get raw cookies.
1077     ListHashSet<Cookie> rawCookiesList;
1078
1079     // If we can't get raw cookies - fall back to String representation
1080     String stringCookiesList;
1081
1082     // Return value to getRawCookies should be the same for every call because
1083     // the return value is platform/network backend specific, and the call will
1084     // always return the same true/false value.
1085     bool rawCookiesImplemented = false;
1086
1087     for (Frame* frame = m_inspectedPage->mainFrame(); frame; frame = frame->tree()->traverseNext(m_inspectedPage->mainFrame())) {
1088         Document* document = frame->document();
1089         const CachedResourceLoader::DocumentResourceMap& allResources = document->cachedResourceLoader()->allCachedResources();
1090         CachedResourceLoader::DocumentResourceMap::const_iterator end = allResources.end();
1091         for (CachedResourceLoader::DocumentResourceMap::const_iterator it = allResources.begin(); it != end; ++it) {
1092             Vector<Cookie> docCookiesList;
1093             rawCookiesImplemented = getRawCookies(document, KURL(ParsedURLString, it->second->url()), docCookiesList);
1094
1095             if (!rawCookiesImplemented) {
1096                 // FIXME: We need duplication checking for the String representation of cookies.
1097                 ExceptionCode ec = 0;
1098                 stringCookiesList += document->cookie(ec);
1099                 // Exceptions are thrown by cookie() in sandboxed frames. That won't happen here
1100                 // because "document" is the document of the main frame of the page.
1101                 ASSERT(!ec);
1102             } else {
1103                 int cookiesSize = docCookiesList.size();
1104                 for (int i = 0; i < cookiesSize; i++) {
1105                     if (!rawCookiesList.contains(docCookiesList[i]))
1106                         rawCookiesList.add(docCookiesList[i]);
1107                 }
1108             }
1109         }
1110     }
1111
1112     if (rawCookiesImplemented)
1113         *cookies = buildArrayForCookies(rawCookiesList);
1114     else
1115         *cookiesString = stringCookiesList;
1116 }
1117
1118 PassRefPtr<InspectorArray> InspectorController::buildArrayForCookies(ListHashSet<Cookie>& cookiesList)
1119 {
1120     RefPtr<InspectorArray> cookies = InspectorArray::create();
1121
1122     ListHashSet<Cookie>::iterator end = cookiesList.end();
1123     ListHashSet<Cookie>::iterator it = cookiesList.begin();
1124     for (int i = 0; it != end; ++it, i++)
1125         cookies->pushObject(buildObjectForCookie(*it));
1126
1127     return cookies;
1128 }
1129
1130 PassRefPtr<InspectorObject> InspectorController::buildObjectForCookie(const Cookie& cookie)
1131 {
1132     RefPtr<InspectorObject> value = InspectorObject::create();
1133     value->setString("name", cookie.name);
1134     value->setString("value", cookie.value);
1135     value->setString("domain", cookie.domain);
1136     value->setString("path", cookie.path);
1137     value->setNumber("expires", cookie.expires);
1138     value->setNumber("size", (cookie.name.length() + cookie.value.length()));
1139     value->setBoolean("httpOnly", cookie.httpOnly);
1140     value->setBoolean("secure", cookie.secure);
1141     value->setBoolean("session", cookie.session);
1142     return value;
1143 }
1144
1145 void InspectorController::deleteCookie(const String& cookieName, const String& domain)
1146 {
1147     for (Frame* frame = m_inspectedPage->mainFrame(); frame; frame = frame->tree()->traverseNext(m_inspectedPage->mainFrame())) {
1148         Document* document = frame->document();
1149         if (document->url().host() != domain)
1150             continue;
1151         const CachedResourceLoader::DocumentResourceMap& allResources = document->cachedResourceLoader()->allCachedResources();
1152         CachedResourceLoader::DocumentResourceMap::const_iterator end = allResources.end();
1153         for (CachedResourceLoader::DocumentResourceMap::const_iterator it = allResources.begin(); it != end; ++it)
1154             WebCore::deleteCookie(document, KURL(ParsedURLString, it->second->url()), cookieName);
1155     }
1156 }
1157
1158 #if ENABLE(DOM_STORAGE)
1159 void InspectorController::didUseDOMStorage(StorageArea* storageArea, bool isLocalStorage, Frame* frame)
1160 {
1161     if (!enabled())
1162         return;
1163
1164     DOMStorageResourcesMap::iterator domStorageEnd = m_domStorageResources.end();
1165     for (DOMStorageResourcesMap::iterator it = m_domStorageResources.begin(); it != domStorageEnd; ++it)
1166         if (it->second->isSameHostAndType(frame, isLocalStorage))
1167             return;
1168
1169     RefPtr<Storage> domStorage = Storage::create(frame, storageArea);
1170     RefPtr<InspectorDOMStorageResource> resource = InspectorDOMStorageResource::create(domStorage.get(), isLocalStorage, frame);
1171
1172     m_domStorageResources.set(resource->id(), resource);
1173
1174     // Resources are only bound while visible.
1175     if (m_frontend)
1176         resource->bind(m_frontend.get());
1177 }
1178
1179 void InspectorController::selectDOMStorage(Storage* storage)
1180 {
1181     ASSERT(storage);
1182     if (!m_frontend)
1183         return;
1184
1185     Frame* frame = storage->frame();
1186     ExceptionCode ec = 0;
1187     bool isLocalStorage = (frame->domWindow()->localStorage(ec) == storage && !ec);
1188     long storageResourceId = 0;
1189     DOMStorageResourcesMap::iterator domStorageEnd = m_domStorageResources.end();
1190     for (DOMStorageResourcesMap::iterator it = m_domStorageResources.begin(); it != domStorageEnd; ++it) {
1191         if (it->second->isSameHostAndType(frame, isLocalStorage)) {
1192             storageResourceId = it->first;
1193             break;
1194         }
1195     }
1196     if (storageResourceId)
1197         m_frontend->selectDOMStorage(storageResourceId);
1198 }
1199
1200 void InspectorController::getDOMStorageEntries(long storageId, RefPtr<InspectorArray>* entries)
1201 {
1202     InspectorDOMStorageResource* storageResource = getDOMStorageResourceForId(storageId);
1203     if (storageResource) {
1204         storageResource->startReportingChangesToFrontend();
1205         Storage* domStorage = storageResource->domStorage();
1206         for (unsigned i = 0; i < domStorage->length(); ++i) {
1207             String name(domStorage->key(i));
1208             String value(domStorage->getItem(name));
1209             RefPtr<InspectorArray> entry = InspectorArray::create();
1210             entry->pushString(name);
1211             entry->pushString(value);
1212             (*entries)->pushArray(entry);
1213         }
1214     }
1215 }
1216
1217 void InspectorController::setDOMStorageItem(long storageId, const String& key, const String& value, bool* success)
1218 {
1219     InspectorDOMStorageResource* storageResource = getDOMStorageResourceForId(storageId);
1220     if (storageResource) {
1221         ExceptionCode exception = 0;
1222         storageResource->domStorage()->setItem(key, value, exception);
1223         *success = !exception;
1224     }
1225 }
1226
1227 void InspectorController::removeDOMStorageItem(long storageId, const String& key, bool* success)
1228 {
1229     InspectorDOMStorageResource* storageResource = getDOMStorageResourceForId(storageId);
1230     if (storageResource) {
1231         storageResource->domStorage()->removeItem(key);
1232         *success = true;
1233     }
1234 }
1235
1236 InspectorDOMStorageResource* InspectorController::getDOMStorageResourceForId(long storageId)
1237 {
1238     DOMStorageResourcesMap::iterator it = m_domStorageResources.find(storageId);
1239     if (it == m_domStorageResources.end())
1240         return 0;
1241     return it->second.get();
1242 }
1243 #endif
1244
1245 #if ENABLE(WEB_SOCKETS)
1246 void InspectorController::didCreateWebSocket(unsigned long identifier, const KURL& requestURL, const KURL& documentURL)
1247 {
1248     if (!enabled())
1249         return;
1250     ASSERT(m_inspectedPage);
1251
1252     if (m_resourceAgent)
1253         m_resourceAgent->didCreateWebSocket(identifier, requestURL);
1254     UNUSED_PARAM(documentURL);
1255 }
1256
1257 void InspectorController::willSendWebSocketHandshakeRequest(unsigned long identifier, const WebSocketHandshakeRequest& request)
1258 {
1259     if (m_resourceAgent)
1260         m_resourceAgent->willSendWebSocketHandshakeRequest(identifier, request);
1261 }
1262
1263 void InspectorController::didReceiveWebSocketHandshakeResponse(unsigned long identifier, const WebSocketHandshakeResponse& response)
1264 {
1265     if (m_resourceAgent)
1266         m_resourceAgent->didReceiveWebSocketHandshakeResponse(identifier, response);
1267 }
1268
1269 void InspectorController::didCloseWebSocket(unsigned long identifier)
1270 {
1271     if (m_resourceAgent)
1272         m_resourceAgent->didCloseWebSocket(identifier);
1273 }
1274 #endif // ENABLE(WEB_SOCKETS)
1275
1276 #if ENABLE(JAVASCRIPT_DEBUGGER)
1277 void InspectorController::addProfile(PassRefPtr<ScriptProfile> prpProfile, unsigned lineNumber, const String& sourceURL)
1278 {
1279     if (!enabled())
1280         return;
1281     m_profilerAgent->addProfile(prpProfile, lineNumber, sourceURL);
1282 }
1283
1284 void InspectorController::addProfileFinishedMessageToConsole(PassRefPtr<ScriptProfile> prpProfile, unsigned lineNumber, const String& sourceURL)
1285 {
1286     m_profilerAgent->addProfileFinishedMessageToConsole(prpProfile, lineNumber, sourceURL);
1287 }
1288
1289 void InspectorController::addStartProfilingMessageToConsole(const String& title, unsigned lineNumber, const String& sourceURL)
1290 {
1291     m_profilerAgent->addStartProfilingMessageToConsole(title, lineNumber, sourceURL);
1292 }
1293
1294
1295 bool InspectorController::isRecordingUserInitiatedProfile() const
1296 {
1297     return m_profilerAgent->isRecordingUserInitiatedProfile();
1298 }
1299
1300 String InspectorController::getCurrentUserInitiatedProfileName(bool incrementProfileNumber)
1301 {
1302     return m_profilerAgent->getCurrentUserInitiatedProfileName(incrementProfileNumber);
1303 }
1304
1305 void InspectorController::startUserInitiatedProfiling()
1306 {
1307     if (!enabled())
1308         return;
1309     m_profilerAgent->startUserInitiatedProfiling();
1310     m_state->setBoolean(InspectorState::userInitiatedProfiling, true);
1311 }
1312
1313 void InspectorController::stopUserInitiatedProfiling()
1314 {
1315     if (!enabled())
1316         return;
1317     m_profilerAgent->stopUserInitiatedProfiling();
1318     m_state->setBoolean(InspectorState::userInitiatedProfiling, false);
1319 }
1320
1321 bool InspectorController::profilerEnabled() const
1322 {
1323     return enabled() && m_profilerAgent->enabled();
1324 }
1325
1326 void InspectorController::enableProfiler(bool always, bool skipRecompile)
1327 {
1328     if (always)
1329         m_state->setBoolean(InspectorState::profilerAlwaysEnabled, true);
1330     m_profilerAgent->enable(skipRecompile);
1331 }
1332
1333 void InspectorController::disableProfiler(bool always)
1334 {
1335     if (always)
1336         m_state->setBoolean(InspectorState::profilerAlwaysEnabled, false);
1337     m_profilerAgent->disable();
1338 }
1339 #endif
1340
1341 #if ENABLE(JAVASCRIPT_DEBUGGER)
1342 void InspectorController::enableDebuggerFromFrontend(bool always)
1343 {
1344     ASSERT(!debuggerEnabled());
1345     if (always)
1346         m_state->setBoolean(InspectorState::debuggerAlwaysEnabled, true);
1347
1348     ASSERT(m_inspectedPage);
1349
1350     m_debuggerAgent = InspectorDebuggerAgent::create(this, m_frontend.get());
1351     restoreStickyBreakpoints();
1352
1353     m_frontend->debuggerWasEnabled();
1354 }
1355
1356 void InspectorController::enableDebugger()
1357 {
1358     if (!enabled())
1359         return;
1360
1361     if (debuggerEnabled())
1362         return;
1363
1364     if (!m_frontend)
1365         m_attachDebuggerWhenShown = true;
1366     else {
1367         m_frontend->attachDebuggerWhenShown();
1368         m_attachDebuggerWhenShown = false;
1369     }
1370 }
1371
1372 void InspectorController::disableDebugger(bool always)
1373 {
1374     if (!enabled())
1375         return;
1376
1377     if (always)
1378         m_state->setBoolean(InspectorState::debuggerAlwaysEnabled, false);
1379
1380     ASSERT(m_inspectedPage);
1381
1382     m_debuggerAgent.clear();
1383
1384     m_attachDebuggerWhenShown = false;
1385
1386     if (m_frontend)
1387         m_frontend->debuggerWasDisabled();
1388 }
1389
1390 void InspectorController::resume()
1391 {
1392     if (m_debuggerAgent)
1393         m_debuggerAgent->resume();
1394 }
1395
1396 void InspectorController::setStickyBreakpoints(PassRefPtr<InspectorObject> breakpoints)
1397 {
1398     m_state->setObject(InspectorState::stickyBreakpoints, breakpoints);
1399 }
1400
1401 void InspectorController::restoreStickyBreakpoints()
1402 {
1403     m_eventListenerBreakpoints.clear();
1404     m_XHRBreakpoints.clear();
1405     m_hasXHRBreakpointWithEmptyURL = false;
1406
1407     RefPtr<InspectorObject> allBreakpoints = m_state->getObject(InspectorState::stickyBreakpoints);
1408     KURL url = m_inspectedPage->mainFrame()->loader()->url();
1409     url.removeFragmentIdentifier();
1410     RefPtr<InspectorArray> breakpoints = allBreakpoints->getArray(url);
1411     if (!breakpoints)
1412         return;
1413     for (unsigned i = 0; i < breakpoints->length(); ++i)
1414         restoreStickyBreakpoint(breakpoints->get(i)->asObject());
1415 }
1416
1417 void InspectorController::restoreStickyBreakpoint(PassRefPtr<InspectorObject> breakpoint)
1418 {
1419     DEFINE_STATIC_LOCAL(String, eventListenerBreakpointType, ("EventListener"));
1420     DEFINE_STATIC_LOCAL(String, javaScriptBreakpointType, ("JS"));
1421     DEFINE_STATIC_LOCAL(String, xhrBreakpointType, ("XHR"));
1422
1423     if (!breakpoint)
1424         return;
1425     String type;
1426     if (!breakpoint->getString("type", &type))
1427         return;
1428     bool enabled;
1429     if (!breakpoint->getBoolean("enabled", &enabled))
1430         return;
1431     RefPtr<InspectorObject> condition = breakpoint->getObject("condition");
1432     if (!condition)
1433         return;
1434
1435     if (type == eventListenerBreakpointType) {
1436         if (!enabled)
1437             return;
1438         String eventName;
1439         if (condition->getString("eventName", &eventName))
1440             setEventListenerBreakpoint(eventName);
1441     } else if (type == javaScriptBreakpointType) {
1442         String url;
1443         if (!condition->getString("url", &url))
1444             return;
1445         double lineNumber;
1446         if (!condition->getNumber("lineNumber", &lineNumber))
1447             return;
1448         String javaScriptCondition;
1449         if (!condition->getString("condition", &javaScriptCondition))
1450             return;
1451         if (m_debuggerAgent)
1452             m_debuggerAgent->setStickyBreakpoint(url, static_cast<unsigned>(lineNumber), javaScriptCondition, enabled);
1453     } else if (type == xhrBreakpointType) {
1454         if (!enabled)
1455             return;
1456         String url;
1457         if (condition->getString("url", &url))
1458             setXHRBreakpoint(url);
1459     }
1460 }
1461
1462 void InspectorController::setEventListenerBreakpoint(const String& eventName)
1463 {
1464     m_eventListenerBreakpoints.add(eventName);
1465 }
1466
1467 void InspectorController::removeEventListenerBreakpoint(const String& eventName)
1468 {
1469     m_eventListenerBreakpoints.remove(eventName);
1470 }
1471
1472 bool InspectorController::hasEventListenerBreakpoint(const String& eventName)
1473 {
1474     return m_eventListenerBreakpoints.contains(eventName);
1475 }
1476
1477 void InspectorController::setXHRBreakpoint(const String& url)
1478 {
1479     if (url.isEmpty())
1480         m_hasXHRBreakpointWithEmptyURL = true;
1481     else
1482         m_XHRBreakpoints.add(url);
1483 }
1484
1485 void InspectorController::removeXHRBreakpoint(const String& url)
1486 {
1487     if (url.isEmpty())
1488         m_hasXHRBreakpointWithEmptyURL = false;
1489     else
1490         m_XHRBreakpoints.remove(url);
1491 }
1492
1493 bool InspectorController::hasXHRBreakpoint(const String& url, String* breakpointURL)
1494 {
1495     if (m_hasXHRBreakpointWithEmptyURL) {
1496         *breakpointURL = "";
1497         return true;
1498     }
1499     for (HashSet<String>::iterator it = m_XHRBreakpoints.begin(); it != m_XHRBreakpoints.end(); ++it) {
1500         if (url.contains(*it)) {
1501             *breakpointURL = *it;
1502             return true;
1503         }
1504     }
1505     return false;
1506 }
1507
1508 #endif
1509
1510 void InspectorController::evaluateForTestInFrontend(long callId, const String& script)
1511 {
1512     if (m_frontend)
1513         m_frontend->evaluateForTestInFrontend(callId, script);
1514     else
1515         m_pendingEvaluateTestCommands.append(pair<long, String>(callId, script));
1516 }
1517
1518 void InspectorController::didEvaluateForTestInFrontend(long callId, const String& jsonResult)
1519 {
1520     ScriptState* scriptState = scriptStateFromPage(debuggerWorld(), m_inspectedPage);
1521     ScriptObject window;
1522     ScriptGlobalObject::get(scriptState, "window", window);
1523     ScriptFunctionCall function(window, "didEvaluateForTestInFrontend");
1524     function.appendArgument(callId);
1525     function.appendArgument(jsonResult);
1526     function.call();
1527 }
1528
1529 static Path quadToPath(const FloatQuad& quad)
1530 {
1531     Path quadPath;
1532     quadPath.moveTo(quad.p1());
1533     quadPath.addLineTo(quad.p2());
1534     quadPath.addLineTo(quad.p3());
1535     quadPath.addLineTo(quad.p4());
1536     quadPath.closeSubpath();
1537     return quadPath;
1538 }
1539
1540 static void drawOutlinedQuad(GraphicsContext& context, const FloatQuad& quad, const Color& fillColor)
1541 {
1542     static const int outlineThickness = 2;
1543     static const Color outlineColor(62, 86, 180, 228);
1544
1545     Path quadPath = quadToPath(quad);
1546
1547     // Clip out the quad, then draw with a 2px stroke to get a pixel
1548     // of outline (because inflating a quad is hard)
1549     {
1550         context.save();
1551         context.clipOut(quadPath);
1552
1553         context.setStrokeThickness(outlineThickness);
1554         context.setStrokeColor(outlineColor, ColorSpaceDeviceRGB);
1555         context.strokePath(quadPath);
1556
1557         context.restore();
1558     }
1559
1560     // Now do the fill
1561     context.setFillColor(fillColor, ColorSpaceDeviceRGB);
1562     context.fillPath(quadPath);
1563 }
1564
1565 static void drawOutlinedQuadWithClip(GraphicsContext& context, const FloatQuad& quad, const FloatQuad& clipQuad, const Color& fillColor)
1566 {
1567     context.save();
1568     Path clipQuadPath = quadToPath(clipQuad);
1569     context.clipOut(clipQuadPath);
1570     drawOutlinedQuad(context, quad, fillColor);
1571     context.restore();
1572 }
1573
1574 static void drawHighlightForBox(GraphicsContext& context, const FloatQuad& contentQuad, const FloatQuad& paddingQuad, const FloatQuad& borderQuad, const FloatQuad& marginQuad)
1575 {
1576     static const Color contentBoxColor(125, 173, 217, 128);
1577     static const Color paddingBoxColor(125, 173, 217, 160);
1578     static const Color borderBoxColor(125, 173, 217, 192);
1579     static const Color marginBoxColor(125, 173, 217, 228);
1580
1581     if (marginQuad != borderQuad)
1582         drawOutlinedQuadWithClip(context, marginQuad, borderQuad, marginBoxColor);
1583     if (borderQuad != paddingQuad)
1584         drawOutlinedQuadWithClip(context, borderQuad, paddingQuad, borderBoxColor);
1585     if (paddingQuad != contentQuad)
1586         drawOutlinedQuadWithClip(context, paddingQuad, contentQuad, paddingBoxColor);
1587
1588     drawOutlinedQuad(context, contentQuad, contentBoxColor);
1589 }
1590
1591 static void drawHighlightForLineBoxesOrSVGRenderer(GraphicsContext& context, const Vector<FloatQuad>& lineBoxQuads)
1592 {
1593     static const Color lineBoxColor(125, 173, 217, 128);
1594
1595     for (size_t i = 0; i < lineBoxQuads.size(); ++i)
1596         drawOutlinedQuad(context, lineBoxQuads[i], lineBoxColor);
1597 }
1598
1599 static inline void convertFromFrameToMainFrame(Frame* frame, IntRect& rect)
1600 {
1601     rect = frame->page()->mainFrame()->view()->windowToContents(frame->view()->contentsToWindow(rect));
1602 }
1603
1604 static inline IntSize frameToMainFrameOffset(Frame* frame)
1605 {
1606     IntPoint mainFramePoint = frame->page()->mainFrame()->view()->windowToContents(frame->view()->contentsToWindow(IntPoint()));
1607     return mainFramePoint - IntPoint();
1608 }
1609
1610 void InspectorController::drawNodeHighlight(GraphicsContext& context) const
1611 {
1612     if (!m_highlightedNode)
1613         return;
1614
1615     RenderObject* renderer = m_highlightedNode->renderer();
1616     Frame* containingFrame = m_highlightedNode->document()->frame();
1617     if (!renderer || !containingFrame)
1618         return;
1619
1620     IntSize mainFrameOffset = frameToMainFrameOffset(containingFrame);
1621     IntRect boundingBox = renderer->absoluteBoundingBoxRect(true);
1622     boundingBox.move(mainFrameOffset);
1623
1624     IntRect titleReferenceBox = boundingBox;
1625
1626     ASSERT(m_inspectedPage);
1627
1628     FrameView* view = m_inspectedPage->mainFrame()->view();
1629     FloatRect overlayRect = view->visibleContentRect();
1630     if (!overlayRect.contains(boundingBox) && !boundingBox.contains(enclosingIntRect(overlayRect)))
1631         overlayRect = view->visibleContentRect();
1632     context.translate(-overlayRect.x(), -overlayRect.y());
1633
1634     // RenderSVGRoot should be highlighted through the isBox() code path, all other SVG elements should just dump their absoluteQuads().
1635 #if ENABLE(SVG)
1636     bool isSVGRenderer = renderer->node() && renderer->node()->isSVGElement() && !renderer->isSVGRoot();
1637 #else
1638     bool isSVGRenderer = false;
1639 #endif
1640
1641     if (renderer->isBox() && !isSVGRenderer) {
1642         RenderBox* renderBox = toRenderBox(renderer);
1643
1644         IntRect contentBox = renderBox->contentBoxRect();
1645
1646         IntRect paddingBox(contentBox.x() - renderBox->paddingLeft(), contentBox.y() - renderBox->paddingTop(),
1647                            contentBox.width() + renderBox->paddingLeft() + renderBox->paddingRight(), contentBox.height() + renderBox->paddingTop() + renderBox->paddingBottom());
1648         IntRect borderBox(paddingBox.x() - renderBox->borderLeft(), paddingBox.y() - renderBox->borderTop(),
1649                           paddingBox.width() + renderBox->borderLeft() + renderBox->borderRight(), paddingBox.height() + renderBox->borderTop() + renderBox->borderBottom());
1650         IntRect marginBox(borderBox.x() - renderBox->marginLeft(), borderBox.y() - renderBox->marginTop(),
1651                           borderBox.width() + renderBox->marginLeft() + renderBox->marginRight(), borderBox.height() + renderBox->marginTop() + renderBox->marginBottom());
1652
1653         titleReferenceBox = marginBox;
1654         titleReferenceBox.move(mainFrameOffset);
1655         titleReferenceBox.move(boundingBox.x(), boundingBox.y());
1656
1657         FloatQuad absContentQuad = renderBox->localToAbsoluteQuad(FloatRect(contentBox));
1658         FloatQuad absPaddingQuad = renderBox->localToAbsoluteQuad(FloatRect(paddingBox));
1659         FloatQuad absBorderQuad = renderBox->localToAbsoluteQuad(FloatRect(borderBox));
1660         FloatQuad absMarginQuad = renderBox->localToAbsoluteQuad(FloatRect(marginBox));
1661
1662         absContentQuad.move(mainFrameOffset);
1663         absPaddingQuad.move(mainFrameOffset);
1664         absBorderQuad.move(mainFrameOffset);
1665         absMarginQuad.move(mainFrameOffset);
1666
1667         drawHighlightForBox(context, absContentQuad, absPaddingQuad, absBorderQuad, absMarginQuad);
1668     } else if (renderer->isRenderInline() || isSVGRenderer) {
1669         // FIXME: We should show margins/padding/border for inlines.
1670         Vector<FloatQuad> lineBoxQuads;
1671         renderer->absoluteQuads(lineBoxQuads);
1672         for (unsigned i = 0; i < lineBoxQuads.size(); ++i)
1673             lineBoxQuads[i] += mainFrameOffset;
1674
1675         drawHighlightForLineBoxesOrSVGRenderer(context, lineBoxQuads);
1676     }
1677
1678     // Draw node title if necessary.
1679
1680     if (!m_highlightedNode->isElementNode())
1681         return;
1682
1683     WebCore::Settings* settings = containingFrame->settings();
1684     drawElementTitle(context, titleReferenceBox, overlayRect, settings);
1685 }
1686
1687 void InspectorController::drawElementTitle(GraphicsContext& context, const IntRect& boundingBox, const FloatRect& overlayRect, WebCore::Settings* settings) const
1688 {
1689     static const int rectInflatePx = 4;
1690     static const int fontHeightPx = 12;
1691     static const int borderWidthPx = 1;
1692     static const Color tooltipBackgroundColor(255, 255, 194, 255);
1693     static const Color tooltipBorderColor(Color::black);
1694     static const Color tooltipFontColor(Color::black);
1695
1696     Element* element = static_cast<Element*>(m_highlightedNode.get());
1697     bool isXHTML = element->document()->isXHTMLDocument();
1698     String nodeTitle = isXHTML ? element->nodeName() : element->nodeName().lower();
1699     const AtomicString& idValue = element->getIdAttribute();
1700     if (!idValue.isNull() && !idValue.isEmpty()) {
1701         nodeTitle += "#";
1702         nodeTitle += idValue;
1703     }
1704     if (element->hasClass() && element->isStyledElement()) {
1705         const SpaceSplitString& classNamesString = static_cast<StyledElement*>(element)->classNames();
1706         size_t classNameCount = classNamesString.size();
1707         if (classNameCount) {
1708             HashSet<AtomicString> usedClassNames;
1709             for (size_t i = 0; i < classNameCount; ++i) {
1710                 const AtomicString& className = classNamesString[i];
1711                 if (usedClassNames.contains(className))
1712                     continue;
1713                 usedClassNames.add(className);
1714                 nodeTitle += ".";
1715                 nodeTitle += className;
1716             }
1717         }
1718     }
1719
1720     Element* highlightedElement = m_highlightedNode->isElementNode() ? static_cast<Element*>(m_highlightedNode.get()) : 0;
1721     nodeTitle += " [";
1722     nodeTitle += String::number(highlightedElement ? highlightedElement->offsetWidth() : boundingBox.width());
1723     nodeTitle.append(static_cast<UChar>(0x00D7)); // &times;
1724     nodeTitle += String::number(highlightedElement ? highlightedElement->offsetHeight() : boundingBox.height());
1725     nodeTitle += "]";
1726
1727     FontDescription desc;
1728     FontFamily family;
1729     family.setFamily(settings->fixedFontFamily());
1730     desc.setFamily(family);
1731     desc.setComputedSize(fontHeightPx);
1732     Font font = Font(desc, 0, 0);
1733     font.update(0);
1734
1735     TextRun nodeTitleRun(nodeTitle);
1736     IntPoint titleBasePoint = boundingBox.bottomLeft();
1737     titleBasePoint.move(rectInflatePx, rectInflatePx);
1738     IntRect titleRect = enclosingIntRect(font.selectionRectForText(nodeTitleRun, titleBasePoint, fontHeightPx));
1739     titleRect.inflate(rectInflatePx);
1740
1741     // The initial offsets needed to compensate for a 1px-thick border stroke (which is not a part of the rectangle).
1742     int dx = -borderWidthPx;
1743     int dy = borderWidthPx;
1744
1745     // If the tip sticks beyond the right of overlayRect, right-align the tip with the said boundary.
1746     if (titleRect.right() > overlayRect.right())
1747         dx = overlayRect.right() - titleRect.right();
1748
1749     // If the tip sticks beyond the left of overlayRect, left-align the tip with the said boundary.
1750     if (titleRect.x() + dx < overlayRect.x())
1751         dx = overlayRect.x() - titleRect.x() - borderWidthPx;
1752
1753     // If the tip sticks beyond the bottom of overlayRect, show the tip at top of bounding box.
1754     if (titleRect.bottom() > overlayRect.bottom()) {
1755         dy = boundingBox.y() - titleRect.bottom() - borderWidthPx;
1756         // If the tip still sticks beyond the bottom of overlayRect, bottom-align the tip with the said boundary.
1757         if (titleRect.bottom() + dy > overlayRect.bottom())
1758             dy = overlayRect.bottom() - titleRect.bottom();
1759     }
1760
1761     // If the tip sticks beyond the top of overlayRect, show the tip at top of overlayRect.
1762     if (titleRect.y() + dy < overlayRect.y())
1763         dy = overlayRect.y() - titleRect.y() + borderWidthPx;
1764
1765     titleRect.move(dx, dy);
1766     context.setStrokeColor(tooltipBorderColor, ColorSpaceDeviceRGB);
1767     context.setStrokeThickness(borderWidthPx);
1768     context.setFillColor(tooltipBackgroundColor, ColorSpaceDeviceRGB);
1769     context.drawRect(titleRect);
1770     context.setFillColor(tooltipFontColor, ColorSpaceDeviceRGB);
1771     context.drawText(font, nodeTitleRun, IntPoint(titleRect.x() + rectInflatePx, titleRect.y() + font.height()));
1772 }
1773
1774 void InspectorController::openInInspectedWindow(const String& url)
1775 {
1776     Frame* mainFrame = m_inspectedPage->mainFrame();
1777
1778     FrameLoadRequest request(mainFrame->document()->securityOrigin(), ResourceRequest(), "_blank");
1779
1780     bool created;
1781     WindowFeatures windowFeatures;
1782     Frame* newFrame = WebCore::createWindow(mainFrame, mainFrame, request, windowFeatures, created);
1783     if (!newFrame)
1784         return;
1785
1786     UserGestureIndicator indicator(DefinitelyProcessingUserGesture);
1787     newFrame->loader()->setOpener(mainFrame);
1788     newFrame->page()->setOpenedByDOM();
1789     newFrame->loader()->changeLocation(mainFrame->document()->securityOrigin(), newFrame->loader()->completeURL(url), "", false, false);
1790 }
1791
1792 void InspectorController::count(const String& title, unsigned lineNumber, const String& sourceID)
1793 {
1794     String identifier = makeString(title, '@', sourceID, ':', String::number(lineNumber));
1795     HashMap<String, unsigned>::iterator it = m_counts.find(identifier);
1796     int count;
1797     if (it == m_counts.end())
1798         count = 1;
1799     else {
1800         count = it->second + 1;
1801         m_counts.remove(it);
1802     }
1803
1804     m_counts.add(identifier, count);
1805
1806     String message = makeString(title, ": ", String::number(count));
1807     addMessageToConsole(JSMessageSource, LogMessageType, LogMessageLevel, message, lineNumber, sourceID);
1808 }
1809
1810 void InspectorController::startTiming(const String& title)
1811 {
1812     m_times.add(title, currentTime() * 1000);
1813 }
1814
1815 bool InspectorController::stopTiming(const String& title, double& elapsed)
1816 {
1817     HashMap<String, double>::iterator it = m_times.find(title);
1818     if (it == m_times.end())
1819         return false;
1820
1821     double startTime = it->second;
1822     m_times.remove(it);
1823
1824     elapsed = currentTime() * 1000 - startTime;
1825     return true;
1826 }
1827
1828 InjectedScript InspectorController::injectedScriptForNodeId(long id)
1829 {
1830
1831     Frame* frame = 0;
1832     if (id) {
1833         ASSERT(m_domAgent);
1834         Node* node = m_domAgent->nodeForId(id);
1835         if (node) {
1836             Document* document = node->ownerDocument();
1837             if (document)
1838                 frame = document->frame();
1839         }
1840     } else
1841         frame = m_inspectedPage->mainFrame();
1842
1843     if (frame)
1844         return m_injectedScriptHost->injectedScriptFor(mainWorldScriptState(frame));
1845
1846     return InjectedScript();
1847 }
1848
1849 void InspectorController::addScriptToEvaluateOnLoad(const String& source)
1850 {
1851     m_scriptsToEvaluateOnLoad.append(source);
1852 }
1853
1854 void InspectorController::removeAllScriptsToEvaluateOnLoad()
1855 {
1856     m_scriptsToEvaluateOnLoad.clear();
1857 }
1858
1859 void InspectorController::setInspectorExtensionAPI(const String& source)
1860 {
1861     m_inspectorExtensionAPI = source;
1862 }
1863
1864 void InspectorController::reloadPage()
1865 {
1866     // FIXME: Why do we set the user gesture indicator here?
1867     UserGestureIndicator indicator(DefinitelyProcessingUserGesture);
1868     m_inspectedPage->mainFrame()->navigationScheduler()->scheduleRefresh();
1869 }
1870
1871 void InspectorController::setExtraHeaders(PassRefPtr<InspectorObject> headers)
1872 {
1873     m_extraHeaders = adoptPtr(new HTTPHeaderMap());
1874     InspectorObject::const_iterator end = headers->end();
1875     for (InspectorObject::const_iterator it = headers->begin(); it != end; ++it) {
1876         String value;
1877         if (!it->second->asString(&value))
1878             continue;
1879         m_extraHeaders->add(it->first, value);
1880     }
1881 }
1882
1883 } // namespace WebCore
1884
1885 #endif // ENABLE(INSPECTOR)