// Copyright 2008, Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "chrome/browser/debugger/debugger_node.h" #include "base/process_util.h" #include "base/string_util.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/render_process_host.h" #include "chrome/browser/render_view_host.h" #include "chrome/browser/web_contents.h" #include "chrome/browser/debugger/debugger_shell.h" DebuggerNode::DebuggerNode() : valid_(true), observing_(false), data_(NULL) { } void DebuggerNode::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { StopObserving(); Invalidate(); } DebuggerNode::~DebuggerNode() { } void DebuggerNode::StopObserving() { if (observing_ && valid_) { NotificationService* service = NotificationService::current(); DCHECK(service); StopObserving(service); observing_ = false; } data_ = NULL; } void DebuggerNode::StopObserving(NotificationService *service) { } v8::Handle DebuggerNode::IndexGetter(uint32_t index, const v8::AccessorInfo& info) { return v8::Undefined(); } v8::Handle DebuggerNode::PropGetter(v8::Handle prop, const v8::AccessorInfo& info) { return v8::Undefined(); } v8::Handle DebuggerNode::Function(const v8::Arguments& args) { return v8::Undefined(); } v8::Handle DebuggerNode::NewInstance() { DebuggerNodeWrapper *wrap = new DebuggerNodeWrapper(this); wrap->AddRef(); v8::Local node = v8::External::New(wrap); // TODO(erikkay): cache these templates? v8::Local templ = v8::FunctionTemplate::New(); if (IsFunction()) { templ->SetCallHandler(&DebuggerNode::NodeFunc, node); v8::Local f = templ->GetFunction(); return f; } v8::Local instance = templ->InstanceTemplate(); if (IsObject()) { instance->SetNamedPropertyHandler(&DebuggerNode::NodeGetter, 0, 0, 0, 0, node); // TODO(erikkay): verify that the interceptor does not have to be // behind the object } if (IsCollection()) { instance->SetIndexedPropertyHandler(&DebuggerNode::NodeIndex, 0, 0, 0, 0, node); } v8::Local ret = instance->NewInstance(); v8::Persistent p = v8::Persistent::New(ret); p.MakeWeak(wrap, &DebuggerShell::HandleWeakReference); return ret; } v8::Handle DebuggerNode::NodeGetter(v8::Local prop, const v8::AccessorInfo& info) { DebuggerNodeWrapper* w = static_cast(v8::External::Cast(*info.Data())->Value()); DebuggerNode* n = w->node(); if (n->IsValid() && n->IsObject()) { return n->PropGetter(prop, info); } else { return v8::Undefined(); } } v8::Handle DebuggerNode::NodeIndex(uint32_t index, const v8::AccessorInfo& info) { DebuggerNodeWrapper* w = static_cast(v8::External::Cast(*info.Data())->Value()); DebuggerNode* n = w->node(); if (n->IsValid() && n->IsCollection()) { return n->IndexGetter(index, info); } else { return v8::Undefined(); } } v8::Handle DebuggerNode::NodeFunc(const v8::Arguments& args) { DebuggerNodeWrapper* w = static_cast(v8::External::Cast(*args.Data())->Value()); DebuggerNode* n = w->node(); if (n->IsValid() && n->IsFunction()) { return n->Function(args); } else { return v8::Undefined(); } } ///////////////////////////////////////////// ChromeNode::ChromeNode(DebuggerShell* debugger) { debugger_ = debugger; } ChromeNode::~ChromeNode() { } v8::Handle ChromeNode::PropGetter(v8::Handle prop, const v8::AccessorInfo& info) { if (prop->Equals(v8::String::New("pid"))) { return v8::Number::New(GetCurrentProcessId()); } else if (prop->Equals(v8::String::New("browser"))) { BrowserListNode *node = BrowserListNode::BrowserList(); return node->NewInstance(); } else if (prop->Equals(v8::String::New("setDebuggerReady"))) { FunctionNode* f = new FunctionNode(DebuggerShell::SetDebuggerReady, debugger_); return f->NewInstance(); } else if (prop->Equals(v8::String::New("setDebuggerBreak"))) { FunctionNode* f = new FunctionNode(DebuggerShell::SetDebuggerBreak, debugger_); return f->NewInstance(); } else if (prop->Equals(v8::String::New("foo"))) { return v8::Undefined(); } else { return prop; } } void ChromeNode::StopObserving(NotificationService *service) { } ///////////////////////////////////////////// BrowserNode::BrowserNode(Browser *b) { data_ = b; NotificationService* service = NotificationService::current(); DCHECK(service); service->AddObserver(this, NOTIFY_BROWSER_CLOSED, Source(b)); observing_ = true; } void BrowserNode::StopObserving(NotificationService *service) { Browser *b = static_cast(data_); service->RemoveObserver(this, NOTIFY_BROWSER_CLOSED, Source(b)); } BrowserNode* BrowserNode::BrowserAtIndex(int index) { if (index >= 0) { BrowserList::const_iterator iter = BrowserList::begin(); for (; (iter != BrowserList::end()) && (index > 0); ++iter, --index); if (iter != BrowserList::end()) { return new BrowserNode(*iter); } } return NULL; } BrowserNode::~BrowserNode() { } Browser* BrowserNode::GetBrowser() { if (IsValid()) { return static_cast(data_); } else { return NULL; } } v8::Handle BrowserNode::PropGetter(v8::Handle prop, const v8::AccessorInfo& info) { Browser *b = GetBrowser(); if (b != NULL) { if (prop->Equals(v8::String::New("title"))) { const TabContents *t = b->GetSelectedTabContents(); std::wstring title = t->GetTitle(); std::string title2 = WideToUTF8(title); return v8::String::New(title2.c_str()); } else if (prop->Equals(v8::String::New("tab"))) { TabListNode* node = TabListNode::TabList(b); return node->NewInstance(); } } return v8::Undefined(); } ///////////////////////////////////////////// BrowserListNode* BrowserListNode::BrowserList() { // TODO(erikkay): cache return new BrowserListNode(); } BrowserListNode::BrowserListNode() { } BrowserListNode::~BrowserListNode() { } v8::Handle BrowserListNode::IndexGetter( uint32_t index, const v8::AccessorInfo& info) { BrowserNode* b = BrowserNode::BrowserAtIndex(index); if (!b) { return v8::Undefined(); } return b->NewInstance(); } void BrowserListNode::StopObserving(NotificationService *service) { } ///////////////////////////////////////////// TabListNode::TabListNode(Browser* b) { data_ = b; NotificationService* service = NotificationService::current(); DCHECK(service); service->AddObserver(this, NOTIFY_BROWSER_CLOSED, Source(b)); observing_ = true; } TabListNode::~TabListNode() { } TabListNode* TabListNode::TabList(Browser* b) { return new TabListNode(b); } Browser* TabListNode::GetBrowser() { if (IsValid()) { return static_cast(data_); } else { return NULL; } } void TabListNode::StopObserving(NotificationService *service) { Browser *b = static_cast(data_); service->RemoveObserver(this, NOTIFY_BROWSER_CLOSED, Source(b)); } v8::Handle TabListNode::IndexGetter(uint32_t index, const v8::AccessorInfo& info) { Browser* b = GetBrowser(); if (b != NULL) { TabContents* tab_contents = b->GetTabContentsAt(index); if (tab_contents) { TabNode* node = new TabNode(tab_contents); return node->NewInstance(); } } return v8::Undefined(); } ///////////////////////////////////////////// TabNode::TabNode(TabContents *c) { data_ = c->controller(); NotificationService* service = NotificationService::current(); DCHECK(service); service->AddObserver(this, NOTIFY_TAB_CLOSING, Source(c->controller())); observing_ = true; } TabNode::~TabNode() { } void TabNode::StopObserving(NotificationService *service) { NavigationController *c = static_cast(data_); service->RemoveObserver(this, NOTIFY_TAB_CLOSING, Source(c)); } TabContents* TabNode::GetTab() { if (IsValid()) { return static_cast(data_)->active_contents(); } else { return NULL; } } v8::Handle TabNode::SendToDebugger(const v8::Arguments& args, WebContents* web) { RenderViewHost* host = web->render_view_host(); if (args.Length() == 1) { std::wstring cmd; v8::Handle obj; obj = args[0]; DebuggerShell::ObjectToString(obj, &cmd); host->SendToDebugger(cmd); } return v8::Undefined(); } v8::Handle TabNode::Attach(const v8::Arguments& args, WebContents* web) { RenderViewHost* host = web->render_view_host(); host->DebugAttach(); RenderProcessHost* proc = host->process(); return v8::Int32::New(process_util::GetProcId(proc->process())); } v8::Handle TabNode::Detach(const v8::Arguments& args, WebContents* web) { RenderViewHost* host = web->render_view_host(); host->DebugDetach(); RenderProcessHost* proc = host->process(); return v8::Int32::New(process_util::GetProcId(proc->process())); } v8::Handle TabNode::Break(const v8::Arguments& args, WebContents* web) { RenderViewHost* host = web->render_view_host(); host->DebugBreak(); return v8::Undefined(); } v8::Handle TabNode::PropGetter(v8::Handle prop, const v8::AccessorInfo& info) { TabContents* t = GetTab(); if (t != NULL) { WebContents* web = t->AsWebContents(); if (prop->Equals(v8::String::New("title"))) { std::wstring title = t->GetTitle(); std::string title2 = WideToUTF8(title); return v8::String::New(title2.c_str()); } else if (web) { if (prop->Equals(v8::String::New("attach"))) { FunctionNode* f = new FunctionNode(TabNode::Attach, web); return f->NewInstance(); } else if (prop->Equals(v8::String::New("detach"))) { FunctionNode* f = new FunctionNode(TabNode::Detach, web); return f->NewInstance(); } else if (prop->Equals(v8::String::New("sendToDebugger"))) { FunctionNode* f = new FunctionNode(TabNode::SendToDebugger, web); return f->NewInstance(); } else if (prop->Equals(v8::String::New("debugBreak"))) { FunctionNode* f = new FunctionNode(TabNode::Break, web); return f->NewInstance(); } } } return v8::Undefined(); } ////////////////////////////////// template v8::Handle FunctionNode::Function(const v8::Arguments &args) { return function_(args, data_); }