Home | History | Annotate | Download | only in chrome_frame
      1 // Copyright (c) 2012 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 #ifndef CHROME_FRAME_URLMON_URL_REQUEST_PRIVATE_H_
      6 #define CHROME_FRAME_URLMON_URL_REQUEST_PRIVATE_H_
      7 
      8 #include <atlbase.h>
      9 #include <atlcom.h>
     10 
     11 #include <string>
     12 
     13 #include "base/gtest_prod_util.h"
     14 #include "base/threading/platform_thread.h"
     15 #include "net/base/net_errors.h"
     16 #include "net/http/http_response_headers.h"
     17 #include "net/url_request/url_request_status.h"
     18 
     19 class UrlmonUrlRequest
     20   : public CComObjectRootEx<CComMultiThreadModel>,
     21     public PluginUrlRequest,
     22     public IServiceProviderImpl<UrlmonUrlRequest>,
     23     public IBindStatusCallback,
     24     public IHttpNegotiate,
     25     public IAuthenticate,
     26     public IHttpSecurity {
     27  public:
     28   virtual bool Start();
     29   virtual void Stop();
     30   virtual bool Read(int bytes_to_read);
     31 
     32   // Special function needed by ActiveDocument::Load()
     33   HRESULT InitPending(const GURL& url, IMoniker* moniker, IBindCtx* bind_ctx,
     34                       bool enable_frame_busting, bool privileged_mode,
     35                       HWND notification_window, IStream* cache);
     36 
     37   // Used from "DownloadRequestInHost".
     38   // Callback will be invoked either right away (if operation is finished) or
     39   // from inside ::OnStopBinding() when it is safe to reuse the bind_context.
     40   typedef base::Callback<void(IMoniker*, IBindCtx*, IStream*, const char*)>
     41       TerminateBindCallback;
     42   void TerminateBind(const TerminateBindCallback& callback);
     43 
     44   // Parent Window for UrlMon error dialogs
     45   void set_parent_window(HWND parent_window) {
     46     parent_window_ = parent_window;
     47   }
     48 
     49   // This function passes information on whether ChromeFrame is running in
     50   // privileged mode.
     51   void set_privileged_mode(bool privileged_mode) {
     52     privileged_mode_ = privileged_mode;
     53   }
     54 
     55   // Returns a string in the form " id: %i Obj: %X URL: %s" which is useful
     56   // to identify request objects in the log.
     57   std::string me() const;
     58 
     59  protected:
     60   UrlmonUrlRequest();
     61   ~UrlmonUrlRequest();
     62 
     63   BEGIN_COM_MAP(UrlmonUrlRequest)
     64     COM_INTERFACE_ENTRY(IHttpNegotiate)
     65     COM_INTERFACE_ENTRY(IServiceProvider)
     66     COM_INTERFACE_ENTRY(IBindStatusCallback)
     67     COM_INTERFACE_ENTRY(IWindowForBindingUI)
     68     COM_INTERFACE_ENTRY(IAuthenticate)
     69     COM_INTERFACE_ENTRY(IHttpSecurity)
     70   END_COM_MAP()
     71 
     72   BEGIN_SERVICE_MAP(UrlmonUrlRequest)
     73     SERVICE_ENTRY(IID_IHttpNegotiate);
     74   END_SERVICE_MAP()
     75 
     76   // IBindStatusCallback implementation
     77   STDMETHOD(OnStartBinding)(DWORD reserved, IBinding* binding);
     78   STDMETHOD(GetPriority)(LONG* priority);
     79   STDMETHOD(OnLowResource)(DWORD reserved);
     80   STDMETHOD(OnProgress)(ULONG progress, ULONG max_progress,
     81       ULONG status_code, LPCWSTR status_text);
     82   STDMETHOD(OnStopBinding)(HRESULT result, LPCWSTR error);
     83   STDMETHOD(GetBindInfo)(DWORD* bind_flags, BINDINFO* bind_info);
     84   STDMETHOD(OnDataAvailable)(DWORD flags, DWORD size, FORMATETC* formatetc,
     85       STGMEDIUM* storage);
     86   STDMETHOD(OnObjectAvailable)(REFIID iid, IUnknown* object);
     87 
     88   // IHttpNegotiate implementation
     89   STDMETHOD(BeginningTransaction)(const wchar_t* url,
     90       const wchar_t* current_headers, DWORD reserved,
     91       wchar_t** additional_headers);
     92   STDMETHOD(OnResponse)(DWORD dwResponseCode, const wchar_t* response_headers,
     93       const wchar_t* request_headers, wchar_t** additional_headers);
     94 
     95   // IWindowForBindingUI implementation. This interface is used typically to
     96   // query the window handle which URLMON uses as the parent of error dialogs.
     97   STDMETHOD(GetWindow)(REFGUID guid_reason, HWND* parent_window);
     98 
     99   // IAuthenticate implementation. Used to return the parent window for the
    100   // dialog displayed by IE for authenticating with a proxy.
    101   STDMETHOD(Authenticate)(HWND* parent_window, LPWSTR* user_name,
    102       LPWSTR* password);
    103 
    104   // IHttpSecurity implementation.
    105   STDMETHOD(OnSecurityProblem)(DWORD problem);
    106 
    107   void set_pending(bool pending) {
    108     pending_ = pending;
    109   }
    110 
    111   bool pending() const {
    112     return pending_;
    113   }
    114 
    115   bool terminate_requested() const {
    116     return !terminate_bind_callback_.is_null();
    117   }
    118 
    119   std::string response_headers() {
    120     return response_headers_;
    121   }
    122 
    123  protected:
    124   void ReleaseBindings();
    125 
    126   HRESULT StartAsyncDownload();
    127   void NotifyDelegateAndDie();
    128   void TerminateTransaction();
    129   static net::Error HresultToNetError(HRESULT hr);
    130 
    131  private:
    132   size_t SendDataToDelegate(size_t bytes);
    133 
    134   // This class simplifies tracking the progress of operation. We have 3 main
    135   // states: DONE, WORKING and ABORTING.
    136   // When in [DONE] or [ABORTING] state, there is additional information
    137   // about the result of operation.
    138   // Start(), SetRedirected(), Cancel() and Done() methods trigger the state
    139   // change. See comments bellow.
    140   class Status {
    141    public:
    142     enum State {DONE, ABORTING, WORKING};
    143     struct Redirection {
    144       Redirection() : http_code(0) { }
    145       int http_code;
    146       std::string utf8_url;
    147     };
    148 
    149     Status() : state_(Status::DONE) {
    150     }
    151 
    152     State get_state() const {
    153       return state_;
    154     }
    155 
    156     // Switch from [DONE] to [WORKING].
    157     void Start() {
    158       DCHECK_EQ(state_, DONE);
    159       state_ = WORKING;
    160     }
    161 
    162     // Save redirection information and switch to [ABORTING] state.
    163     // Assumes binding_->Abort() will be called!
    164     void SetRedirected(int http_code, const std::string& utf8_url) {
    165       DCHECK_EQ(state_, WORKING);
    166       DCHECK_EQ(result_.status(), net::URLRequestStatus::SUCCESS);
    167       redirect_.utf8_url = utf8_url;
    168 
    169       // At times we receive invalid redirect codes like 0, 200, etc. We
    170       // default to 302 in this case.
    171       redirect_.http_code = http_code;
    172       if (!net::HttpResponseHeaders::IsRedirectResponseCode(http_code))
    173         redirect_.http_code = 302;
    174 
    175       state_ = ABORTING;
    176     }
    177 
    178     // Set the result as net::URLRequestStatus::CANCELED.
    179     // Switch to [ABORTING] state (if not already in that state).
    180     void Cancel() {
    181       if (state_ == DONE)
    182         return;
    183 
    184       if (state_ == WORKING) {
    185         state_ =  ABORTING;
    186       } else {
    187         // state_ == ABORTING
    188         redirect_.http_code = 0;
    189         redirect_.utf8_url.clear();
    190       }
    191 
    192       set_result(net::URLRequestStatus::CANCELED, 0);
    193     }
    194 
    195     void Done() {
    196       state_ = DONE;
    197     }
    198 
    199     bool was_redirected() const {
    200       return redirect_.http_code != 0;
    201     }
    202 
    203     const Redirection& get_redirection() const {
    204       return redirect_;
    205     }
    206 
    207     const net::URLRequestStatus& get_result() const {
    208       return result_;
    209     }
    210 
    211     void set_result(net::URLRequestStatus::Status status, int error) {
    212       result_.set_status(status);
    213       result_.set_error(error);
    214     }
    215 
    216     void set_result(HRESULT hr) {
    217       result_.set_status(FAILED(hr)? net::URLRequestStatus::FAILED:
    218                                      net::URLRequestStatus::SUCCESS);
    219       result_.set_error(HresultToNetError(hr));
    220     }
    221 
    222    private:
    223     Redirection redirect_;
    224     State state_;
    225     net::URLRequestStatus result_;
    226   };
    227 
    228   Status status_;
    229   base::win::ScopedComPtr<IBinding> binding_;
    230   base::win::ScopedComPtr<IMoniker> moniker_;
    231   base::win::ScopedComPtr<IBindCtx> bind_context_;
    232   base::win::ScopedComPtr<IStream> cache_;
    233   base::win::ScopedComPtr<IStream> pending_data_;
    234 
    235   size_t pending_read_size_;
    236   base::PlatformThreadId thread_;
    237   HWND parent_window_;
    238   bool headers_received_;
    239   int calling_delegate_;  // re-entrancy protection.
    240   // Set to true if the ChromeFrame instance is running in privileged mode.
    241   bool privileged_mode_;
    242   bool pending_;
    243   TerminateBindCallback terminate_bind_callback_;
    244   std::string response_headers_;
    245   // Defaults to true and indicates whether we want to keep the original
    246   // transaction alive when we receive the last data notification from
    247   // urlmon.
    248   bool is_expecting_download_;
    249   // Set to true if the Urlmon transaction object needs to be cleaned up
    250   // when this object is destroyed. Happens if we return
    251   // INET_E_TERMINATE_BIND from OnDataAvailable in the last data notification.
    252   bool cleanup_transaction_;
    253   // Copy of the request headers.
    254   std::string request_headers_;
    255 
    256   DISALLOW_COPY_AND_ASSIGN(UrlmonUrlRequest);
    257 };
    258 
    259 #endif  // CHROME_FRAME_URLMON_URL_REQUEST_PRIVATE_H_
    260