OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "chrome/browser/dom_ui/net_internals_ui.h" |
| 6 |
| 7 #include "app/resource_bundle.h" |
| 8 #include "base/singleton.h" |
| 9 #include "base/string_piece.h" |
| 10 #include "base/values.h" |
| 11 #include "chrome/browser/dom_ui/chrome_url_data_manager.h" |
| 12 #include "chrome/browser/chrome_thread.h" |
| 13 #include "chrome/common/url_constants.h" |
| 14 |
| 15 #include "grit/browser_resources.h" |
| 16 |
| 17 namespace { |
| 18 |
| 19 class NetInternalsHTMLSource : public ChromeURLDataManager::DataSource { |
| 20 public: |
| 21 NetInternalsHTMLSource(); |
| 22 |
| 23 // Called when the network layer has requested a resource underneath |
| 24 // the path we registered. |
| 25 virtual void StartDataRequest(const std::string& path, |
| 26 bool is_off_the_record, |
| 27 int request_id); |
| 28 virtual std::string GetMimeType(const std::string&) const; |
| 29 |
| 30 private: |
| 31 ~NetInternalsHTMLSource() {} |
| 32 DISALLOW_COPY_AND_ASSIGN(NetInternalsHTMLSource); |
| 33 }; |
| 34 |
| 35 // This class receives javascript messages from the renderer. |
| 36 // Note that the DOMUI infrastructure runs on the UI thread, therefore all of |
| 37 // this class's methods are expected to run on the UI thread. |
| 38 // |
| 39 // Since the network code we want to run lives on the IO thread, we proxy |
| 40 // everything over to NetInternalsMessageHandler::IOThreadImpl, which runs |
| 41 // on the IO thread. |
| 42 // |
| 43 // TODO(eroman): Can we start on the IO thread to begin with? |
| 44 class NetInternalsMessageHandler |
| 45 : public DOMMessageHandler, |
| 46 public base::SupportsWeakPtr<NetInternalsMessageHandler> { |
| 47 public: |
| 48 NetInternalsMessageHandler(); |
| 49 virtual ~NetInternalsMessageHandler(); |
| 50 |
| 51 // DOMMessageHandler implementation. |
| 52 virtual DOMMessageHandler* Attach(DOMUI* dom_ui); |
| 53 virtual void RegisterMessages(); |
| 54 |
| 55 // Executes the javascript function |function_name| in the renderer, passing |
| 56 // it the argument |value|. |
| 57 void CallJavascriptFunction(const std::wstring& function_name, |
| 58 const Value& value); |
| 59 |
| 60 private: |
| 61 class IOThreadImpl; |
| 62 |
| 63 // This is the "real" message handler, which lives on the IO thread. |
| 64 scoped_refptr<IOThreadImpl> proxy_; |
| 65 |
| 66 DISALLOW_COPY_AND_ASSIGN(NetInternalsMessageHandler); |
| 67 }; |
| 68 |
| 69 // This class is the "real" message handler. With the exception of being |
| 70 // allocated and destroyed on the UI thread, its methods are expected to be |
| 71 // called from the IO thread. |
| 72 class NetInternalsMessageHandler::IOThreadImpl |
| 73 : public base::RefCountedThreadSafe< |
| 74 NetInternalsMessageHandler::IOThreadImpl, |
| 75 ChromeThread::DeleteOnUIThread> { |
| 76 public: |
| 77 // Type for methods that can be used as MessageHandler callbacks. |
| 78 typedef void (IOThreadImpl::*MessageHandler)(const Value*); |
| 79 |
| 80 // Creates a proxy for |handler| that will live on the IO thread. |
| 81 // |handler| is a weak pointer, since it is possible for the DOMMessageHandler |
| 82 // to be deleted on the UI thread while we were executing on the IO thread. |
| 83 explicit IOThreadImpl( |
| 84 const base::WeakPtr<NetInternalsMessageHandler>& handler); |
| 85 |
| 86 ~IOThreadImpl(); |
| 87 |
| 88 // Creates a callback that will run |method| on the IO thread. |
| 89 // |
| 90 // This can be used with DOMUI::RegisterMessageCallback() to bind to a method |
| 91 // on the IO thread. |
| 92 DOMUI::MessageCallback* CreateCallback(MessageHandler method); |
| 93 |
| 94 // Called once the DOMUI has attached to the renderer, on the IO thread. |
| 95 void Attach(); |
| 96 |
| 97 // Called once the DOMUI has been deleted (i.e. renderer went away), on the |
| 98 // IO thread. |
| 99 void Detach(); |
| 100 |
| 101 //-------------------------------- |
| 102 // Javascript message handlers: |
| 103 //-------------------------------- |
| 104 |
| 105 // TODO(eroman): This is temporary! |
| 106 void OnTestMessage(const Value* value); |
| 107 |
| 108 private: |
| 109 class CallbackHelper; |
| 110 |
| 111 // Helper that runs |method| with |arg|, and deletes |arg| on completion. |
| 112 void DispatchToMessageHandler(Value* arg, MessageHandler method); |
| 113 |
| 114 // Helper that executes |function_name| in the attached renderer. |
| 115 // The function takes ownership of |arg|. |
| 116 void CallJavascriptFunction(const std::wstring& function_name, |
| 117 Value* arg); |
| 118 |
| 119 private: |
| 120 // Pointer to the UI-thread message handler. Only access this from |
| 121 // the UI thread. |
| 122 base::WeakPtr<NetInternalsMessageHandler> handler_; |
| 123 friend class base::RefCountedThreadSafe<IOThreadImpl>; |
| 124 }; |
| 125 |
| 126 // Helper class for a DOMUI::MessageCallback which when excuted calls |
| 127 // instance->*method(value) on the IO thread. |
| 128 class NetInternalsMessageHandler::IOThreadImpl::CallbackHelper |
| 129 : public DOMUI::MessageCallback { |
| 130 public: |
| 131 CallbackHelper(IOThreadImpl* instance, IOThreadImpl::MessageHandler method) |
| 132 : instance_(instance), |
| 133 method_(method) { |
| 134 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| 135 } |
| 136 |
| 137 virtual void RunWithParams(const Tuple1<const Value*>& params) { |
| 138 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| 139 |
| 140 // We need to make a copy of the value in order to pass it over to the IO |
| 141 // thread. We will delete this in IOThreadImpl::DispatchMessageHandler(). |
| 142 Value* value_copy = params.a ? params.a->DeepCopy() : NULL; |
| 143 |
| 144 if (!ChromeThread::PostTask( |
| 145 ChromeThread::IO, FROM_HERE, |
| 146 NewRunnableMethod(instance_.get(), |
| 147 &IOThreadImpl::DispatchToMessageHandler, |
| 148 value_copy, method_))) { |
| 149 // Failed posting the task, avoid leaking |value_copy|. |
| 150 delete value_copy; |
| 151 } |
| 152 } |
| 153 |
| 154 private: |
| 155 scoped_refptr<IOThreadImpl> instance_; |
| 156 IOThreadImpl::MessageHandler method_; |
| 157 }; |
| 158 |
| 159 //////////////////////////////////////////////////////////////////////////////// |
| 160 // |
| 161 // NetInternalsHTMLSource |
| 162 // |
| 163 //////////////////////////////////////////////////////////////////////////////// |
| 164 |
| 165 NetInternalsHTMLSource::NetInternalsHTMLSource() |
| 166 : DataSource(chrome::kChromeUINetInternalsHost, MessageLoop::current()) { |
| 167 } |
| 168 |
| 169 void NetInternalsHTMLSource::StartDataRequest(const std::string& path, |
| 170 bool is_off_the_record, |
| 171 int request_id) { |
| 172 // Serve up the HTML contained in the resource bundle. |
| 173 base::StringPiece html( |
| 174 ResourceBundle::GetSharedInstance().GetRawDataResource( |
| 175 IDR_NET_INTERNALS_HTML)); |
| 176 |
| 177 scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes); |
| 178 html_bytes->data.resize(html.size()); |
| 179 std::copy(html.begin(), html.end(), html_bytes->data.begin()); |
| 180 |
| 181 SendResponse(request_id, html_bytes); |
| 182 } |
| 183 |
| 184 std::string NetInternalsHTMLSource::GetMimeType(const std::string&) const { |
| 185 return "text/html"; |
| 186 } |
| 187 |
| 188 //////////////////////////////////////////////////////////////////////////////// |
| 189 // |
| 190 // NetInternalsMessageHandler |
| 191 // |
| 192 //////////////////////////////////////////////////////////////////////////////// |
| 193 |
| 194 NetInternalsMessageHandler::NetInternalsMessageHandler() {} |
| 195 |
| 196 NetInternalsMessageHandler::~NetInternalsMessageHandler() { |
| 197 if (proxy_) { |
| 198 // Notify the handler on the IO thread that the renderer is gone. |
| 199 ChromeThread::PostTask(ChromeThread::IO, FROM_HERE, |
| 200 NewRunnableMethod(proxy_.get(), &IOThreadImpl::Detach)); |
| 201 } |
| 202 } |
| 203 |
| 204 DOMMessageHandler* NetInternalsMessageHandler::Attach(DOMUI* dom_ui) { |
| 205 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| 206 proxy_ = new IOThreadImpl(this->AsWeakPtr()); |
| 207 |
| 208 DOMMessageHandler* result = DOMMessageHandler::Attach(dom_ui); |
| 209 |
| 210 // Notify the handler on the IO thread that a renderer is attached. |
| 211 ChromeThread::PostTask(ChromeThread::IO, FROM_HERE, |
| 212 NewRunnableMethod(proxy_.get(), &IOThreadImpl::Attach)); |
| 213 |
| 214 return result; |
| 215 } |
| 216 |
| 217 void NetInternalsMessageHandler::RegisterMessages() { |
| 218 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| 219 |
| 220 // TODO(eroman): Register message handlers here. |
| 221 dom_ui_->RegisterMessageCallback("testMessage", |
| 222 proxy_->CreateCallback(&IOThreadImpl::OnTestMessage)); |
| 223 } |
| 224 |
| 225 void NetInternalsMessageHandler::CallJavascriptFunction( |
| 226 const std::wstring& function_name, |
| 227 const Value& value) { |
| 228 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| 229 dom_ui_->CallJavascriptFunction(function_name, value); |
| 230 } |
| 231 |
| 232 //////////////////////////////////////////////////////////////////////////////// |
| 233 // |
| 234 // NetInternalsMessageHandler::IOThreadImpl |
| 235 // |
| 236 //////////////////////////////////////////////////////////////////////////////// |
| 237 |
| 238 NetInternalsMessageHandler::IOThreadImpl::IOThreadImpl( |
| 239 const base::WeakPtr<NetInternalsMessageHandler>& handler) |
| 240 : handler_(handler) { |
| 241 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| 242 } |
| 243 |
| 244 NetInternalsMessageHandler::IOThreadImpl::~IOThreadImpl() { |
| 245 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| 246 } |
| 247 |
| 248 DOMUI::MessageCallback* |
| 249 NetInternalsMessageHandler::IOThreadImpl::CreateCallback( |
| 250 MessageHandler method) { |
| 251 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
| 252 return new CallbackHelper(this, method); |
| 253 } |
| 254 |
| 255 void NetInternalsMessageHandler::IOThreadImpl::Attach() { |
| 256 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); |
| 257 // TODO(eroman): Register with network stack to observe events. |
| 258 } |
| 259 |
| 260 void NetInternalsMessageHandler::IOThreadImpl::Detach() { |
| 261 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); |
| 262 // TODO(eroman): Unregister with network stack to observe events. |
| 263 } |
| 264 |
| 265 void NetInternalsMessageHandler::IOThreadImpl::OnTestMessage( |
| 266 const Value* value) { |
| 267 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); |
| 268 |
| 269 // TODO(eroman): This is just a temporary method, to see something in |
| 270 // action. We expect to have been called with an array |
| 271 // containing 1 string, and print it to the screen. |
| 272 std::string str; |
| 273 if (value && value->GetType() == Value::TYPE_LIST) { |
| 274 const ListValue* list_value = static_cast<const ListValue*>(value); |
| 275 Value* list_member; |
| 276 if (list_value->Get(0, &list_member) && |
| 277 list_member->GetType() == Value::TYPE_STRING) { |
| 278 const StringValue* string_value = |
| 279 static_cast<const StringValue*>(list_member); |
| 280 string_value->GetAsString(&str); |
| 281 } |
| 282 } |
| 283 |
| 284 CallJavascriptFunction( |
| 285 L"log", |
| 286 Value::CreateStringValue("Browser received testMessage: " + str)); |
| 287 } |
| 288 |
| 289 void NetInternalsMessageHandler::IOThreadImpl::DispatchToMessageHandler( |
| 290 Value* arg, MessageHandler method) { |
| 291 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); |
| 292 (this->*method)(arg); |
| 293 delete arg; |
| 294 } |
| 295 |
| 296 void NetInternalsMessageHandler::IOThreadImpl::CallJavascriptFunction( |
| 297 const std::wstring& function_name, |
| 298 Value* arg) { |
| 299 if (ChromeThread::CurrentlyOn(ChromeThread::UI)) { |
| 300 if (handler_) { |
| 301 // We check |handler_| in case it was deleted on the UI thread earlier |
| 302 // while we were running on the IO thread. |
| 303 handler_->CallJavascriptFunction(function_name, *arg); |
| 304 } |
| 305 delete arg; |
| 306 return; |
| 307 } |
| 308 |
| 309 |
| 310 // Otherwise if we were called from the IO thread, bridge the request over to |
| 311 // the UI thread. |
| 312 |
| 313 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); |
| 314 if (!ChromeThread::PostTask( |
| 315 ChromeThread::UI, FROM_HERE, |
| 316 NewRunnableMethod( |
| 317 this, |
| 318 &IOThreadImpl::CallJavascriptFunction, |
| 319 function_name, arg))) { |
| 320 // Failed posting the task, avoid leaking. |
| 321 delete arg; |
| 322 } |
| 323 |
| 324 } |
| 325 |
| 326 } // namespace |
| 327 |
| 328 |
| 329 //////////////////////////////////////////////////////////////////////////////// |
| 330 // |
| 331 // NetInternalsUI |
| 332 // |
| 333 //////////////////////////////////////////////////////////////////////////////// |
| 334 |
| 335 NetInternalsUI::NetInternalsUI(TabContents* contents) : DOMUI(contents) { |
| 336 AddMessageHandler((new NetInternalsMessageHandler())->Attach(this)); |
| 337 |
| 338 NetInternalsHTMLSource* html_source = new NetInternalsHTMLSource(); |
| 339 |
| 340 // Set up the chrome://net-internals/ source. |
| 341 ChromeThread::PostTask( |
| 342 ChromeThread::IO, FROM_HERE, |
| 343 NewRunnableMethod( |
| 344 Singleton<ChromeURLDataManager>::get(), |
| 345 &ChromeURLDataManager::AddDataSource, |
| 346 make_scoped_refptr(html_source))); |
| 347 } |
OLD | NEW |