OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2014 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/ui/webui/print_preview/hidden_web_contents.h" |
| 6 |
| 7 #include <string> |
| 8 |
| 9 #include "chrome/browser/chrome_notification_types.h" |
| 10 #include "chrome/browser/profiles/profile.h" |
| 11 #include "chrome/browser/ui/tab_helpers.h" |
| 12 #include "chrome/browser/ui/web_contents_sizer.h" |
| 13 #include "chrome/common/prerender_messages.h" |
| 14 #include "content/public/browser/browser_thread.h" |
| 15 #include "content/public/browser/notification_service.h" |
| 16 #include "content/public/browser/render_frame_host.h" |
| 17 #include "content/public/browser/render_process_host.h" |
| 18 #include "content/public/browser/render_view_host.h" |
| 19 #include "content/public/browser/session_storage_namespace.h" |
| 20 #include "content/public/browser/web_contents.h" |
| 21 #include "content/public/browser/web_contents_delegate.h" |
| 22 |
| 23 using content::OpenURLParams; |
| 24 using content::RenderViewHost; |
| 25 using content::SessionStorageNamespace; |
| 26 using content::WebContents; |
| 27 |
| 28 class HiddenWebContents::WebContentsDelegateImpl |
| 29 : public content::WebContentsDelegate { |
| 30 public: |
| 31 explicit WebContentsDelegateImpl(HiddenWebContents* hidden_web_contents) |
| 32 : hidden_web_contents_(hidden_web_contents) { |
| 33 } |
| 34 |
| 35 // content::WebContentsDelegate implementation: |
| 36 WebContents* OpenURLFromTab(WebContents* source, |
| 37 const OpenURLParams& params) override { |
| 38 // |OpenURLFromTab| is typically called when a frame performs a navigation |
| 39 // that requires the browser to perform the transition instead of WebKit. |
| 40 // Examples include rendering a site that redirects to an app URL, |
| 41 // or if --enable-strict-site-isolation is specified and the rendered |
| 42 // frame redirects to a different origin. |
| 43 hidden_web_contents_->Destroy(); |
| 44 return NULL; |
| 45 } |
| 46 |
| 47 void CloseContents(content::WebContents* contents) override { |
| 48 hidden_web_contents_->Destroy(); |
| 49 } |
| 50 |
| 51 void CanDownload(RenderViewHost* render_view_host, |
| 52 const GURL& url, |
| 53 const std::string& request_method, |
| 54 const base::Callback<void(bool)>& callback) override { |
| 55 hidden_web_contents_->Destroy(); |
| 56 // Cancel the download. |
| 57 callback.Run(false); |
| 58 } |
| 59 |
| 60 bool ShouldCreateWebContents( |
| 61 WebContents* web_contents, |
| 62 int route_id, |
| 63 int main_frame_route_id, |
| 64 WindowContainerType window_container_type, |
| 65 const base::string16& frame_name, |
| 66 const GURL& target_url, |
| 67 const std::string& partition_id, |
| 68 SessionStorageNamespace* session_storage_namespace) override { |
| 69 // Since we don't want to permit child windows that would have a |
| 70 // window.opener property, terminate rendering. |
| 71 hidden_web_contents_->Destroy(); |
| 72 // Cancel the popup. |
| 73 return false; |
| 74 } |
| 75 |
| 76 bool OnGoToEntryOffset(int offset) override { |
| 77 // This isn't allowed because the history merge operation |
| 78 // does not work if there are renderer issued challenges. |
| 79 // TODO(cbentzel): Cancel in this case? May not need to do |
| 80 // since render-issued offset navigations are not guaranteed, |
| 81 // but indicates that the page cares about the history. |
| 82 return false; |
| 83 } |
| 84 |
| 85 bool ShouldSuppressDialogs(WebContents* source) override { |
| 86 // We still want to show the user the message when they navigate to this |
| 87 // page, so cancel this render. |
| 88 hidden_web_contents_->Destroy(); |
| 89 // Always suppress JavaScript messages if they're triggered by a page being |
| 90 // rendered. |
| 91 return true; |
| 92 } |
| 93 |
| 94 void RegisterProtocolHandler(WebContents* web_contents, |
| 95 const std::string& protocol, |
| 96 const GURL& url, |
| 97 bool user_gesture) override { |
| 98 hidden_web_contents_->Destroy(); |
| 99 } |
| 100 |
| 101 gfx::Size GetSizeForNewRenderView(WebContents* web_contents) const override { |
| 102 // Have to set the size of the RenderView on initialization to be sure it is |
| 103 // set before the RenderView is hidden on all platforms (esp. Android). |
| 104 return hidden_web_contents_->size_; |
| 105 } |
| 106 |
| 107 private: |
| 108 HiddenWebContents* hidden_web_contents_; |
| 109 }; |
| 110 |
| 111 void HiddenWebContents::Observer::OnFinishedLoad( |
| 112 HiddenWebContents* contents) { |
| 113 } |
| 114 |
| 115 HiddenWebContents::Observer::Observer() { |
| 116 } |
| 117 |
| 118 HiddenWebContents::Observer::~Observer() { |
| 119 } |
| 120 |
| 121 HiddenWebContents::HiddenWebContents( |
| 122 Profile* profile, |
| 123 const GURL& url) |
| 124 : rendering_has_started_(false), |
| 125 session_storage_namespace_id_(-1), |
| 126 url_(url), |
| 127 profile_(profile), |
| 128 has_stopped_loading_(false), |
| 129 has_finished_loading_(false), |
| 130 rendering_has_been_cancelled_(false) { |
| 131 } |
| 132 |
| 133 // static |
| 134 bool HiddenWebContents::IsValidUrl(const GURL& url) { |
| 135 return url.SchemeIs("chrome-distiller"); |
| 136 } |
| 137 |
| 138 void HiddenWebContents::StartRendering( |
| 139 const gfx::Size& size, |
| 140 SessionStorageNamespace* session_storage_namespace) { |
| 141 DCHECK(profile_ != NULL); |
| 142 DCHECK(!size.IsEmpty()); |
| 143 DCHECK(!rendering_has_started_); |
| 144 DCHECK(web_contents_.get() == NULL); |
| 145 DCHECK(size_.IsEmpty()); |
| 146 |
| 147 if (!HiddenWebContents::IsValidUrl(url_)) { |
| 148 Destroy(); |
| 149 return; |
| 150 } |
| 151 |
| 152 session_storage_namespace_id_ = session_storage_namespace->id(); |
| 153 size_ = size; |
| 154 |
| 155 rendering_has_started_ = true; |
| 156 |
| 157 web_contents_.reset(CreateWebContents(session_storage_namespace)); |
| 158 TabHelpers::AttachTabHelpers(web_contents_.get()); |
| 159 content::WebContentsObserver::Observe(web_contents_.get()); |
| 160 |
| 161 web_contents_delegate_.reset(new WebContentsDelegateImpl(this)); |
| 162 web_contents_.get()->SetDelegate(web_contents_delegate_.get()); |
| 163 |
| 164 // Set the size of the hidden WebContents. |
| 165 ResizeWebContents(web_contents_.get(), size_); |
| 166 |
| 167 // Close ourselves when the application is shutting down. |
| 168 notification_registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING, |
| 169 content::NotificationService::AllSources()); |
| 170 |
| 171 // Register to inform new RenderViews that we're rendering. |
| 172 notification_registrar_.Add( |
| 173 this, content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED, |
| 174 content::Source<WebContents>(web_contents_.get())); |
| 175 |
| 176 content::NavigationController::LoadURLParams load_url_params(url_); |
| 177 load_url_params.transition_type = ui::PAGE_TRANSITION_LINK; |
| 178 web_contents_.get()->GetController().LoadURLWithParams(load_url_params); |
| 179 } |
| 180 |
| 181 HiddenWebContents::~HiddenWebContents() { |
| 182 if (web_contents_.get()) { |
| 183 web_contents_->SetDelegate(NULL); |
| 184 content::WebContentsObserver::Observe(NULL); |
| 185 } |
| 186 } |
| 187 |
| 188 void HiddenWebContents::AddObserver(Observer* observer) { |
| 189 observer_list_.AddObserver(observer); |
| 190 } |
| 191 |
| 192 void HiddenWebContents::RemoveObserver(Observer* observer) { |
| 193 observer_list_.RemoveObserver(observer); |
| 194 } |
| 195 |
| 196 void HiddenWebContents::Observe(int type, |
| 197 const content::NotificationSource& source, |
| 198 const content::NotificationDetails& details) { |
| 199 switch (type) { |
| 200 // TODO(davidben): Try to remove this in favor of relying on |
| 201 // FINAL_STATUS_PROFILE_DESTROYED. |
| 202 case chrome::NOTIFICATION_APP_TERMINATING: |
| 203 Destroy(); |
| 204 return; |
| 205 |
| 206 case content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED: { |
| 207 if (web_contents_.get()) { |
| 208 DCHECK_EQ(content::Source<WebContents>(source).ptr(), |
| 209 web_contents_.get()); |
| 210 |
| 211 // Make sure the size of the RenderViewHost has been passed to the new |
| 212 // RenderView. Otherwise, the size may not be sent until the |
| 213 // RenderViewReady event makes it from the render process to the UI |
| 214 // thread of the browser process. When the RenderView receives its |
| 215 // size, is also sets itself to be visible, which would then break the |
| 216 // visibility API. |
| 217 content::Details<RenderViewHost> new_render_view_host(details); |
| 218 new_render_view_host->WasResized(); |
| 219 web_contents_->WasHidden(); |
| 220 } |
| 221 break; |
| 222 } |
| 223 |
| 224 default: |
| 225 NOTREACHED() << "Unexpected notification sent."; |
| 226 break; |
| 227 } |
| 228 } |
| 229 |
| 230 WebContents* HiddenWebContents::CreateWebContents( |
| 231 SessionStorageNamespace* session_storage_namespace) { |
| 232 // TODO(ajwong): Remove the temporary map once prerendering is aware of |
| 233 // multiple session storage namespaces per tab. |
| 234 content::SessionStorageNamespaceMap session_storage_namespace_map; |
| 235 session_storage_namespace_map[std::string()] = session_storage_namespace; |
| 236 return WebContents::CreateWithSessionStorage( |
| 237 WebContents::CreateParams(profile_), session_storage_namespace_map); |
| 238 } |
| 239 |
| 240 void HiddenWebContents::NotifyFail() { |
| 241 FOR_EACH_OBSERVER(Observer, observer_list_, OnFail(this)); |
| 242 observer_list_.Clear(); |
| 243 } |
| 244 |
| 245 void HiddenWebContents::NotifyFinishedLoad() { |
| 246 FOR_EACH_OBSERVER(Observer, observer_list_, OnFinishedLoad(this)); |
| 247 } |
| 248 |
| 249 void HiddenWebContents::RenderProcessGone(base::TerminationStatus status) { |
| 250 Destroy(); |
| 251 } |
| 252 |
| 253 void HiddenWebContents::RenderFrameCreated( |
| 254 content::RenderFrameHost* render_frame_host) { |
| 255 // When a new RenderFrame is created for a hidden rendering WebContents, tell |
| 256 // the new RenderFrame it's being used for prerendering before any |
| 257 // navigations occur. Note that this is always triggered before the first |
| 258 // navigation, so there's no need to send the message just after the |
| 259 // WebContents is created. |
| 260 render_frame_host->Send(new PrerenderMsg_SetIsPrerendering( |
| 261 render_frame_host->GetRoutingID(), true)); |
| 262 } |
| 263 |
| 264 void HiddenWebContents::DidStopLoading() { |
| 265 has_stopped_loading_ = true; |
| 266 } |
| 267 |
| 268 void HiddenWebContents::DocumentLoadedInFrame( |
| 269 content::RenderFrameHost* render_frame_host) { |
| 270 } |
| 271 |
| 272 void HiddenWebContents::DidStartProvisionalLoadForFrame( |
| 273 content::RenderFrameHost* render_frame_host, |
| 274 const GURL& validated_url, |
| 275 bool is_error_page, |
| 276 bool is_iframe_srcdoc) { |
| 277 if (!render_frame_host->GetParent()) { |
| 278 // Usually, this event fires if the user clicks or enters a new URL. |
| 279 // Neither of these can happen in the case of an hidden renderer. |
| 280 // So the cause is: Some JavaScript caused a new URL to be loaded. In that |
| 281 // case, the spinner would start again in the browser, so we must reset |
| 282 // has_stopped_loading_ so that the spinner won't be stopped. |
| 283 has_stopped_loading_ = false; |
| 284 has_finished_loading_ = false; |
| 285 } |
| 286 } |
| 287 |
| 288 void HiddenWebContents::DidFinishLoad( |
| 289 content::RenderFrameHost* render_frame_host, |
| 290 const GURL& validated_url) { |
| 291 if (!render_frame_host->GetParent()) |
| 292 has_finished_loading_ = true; |
| 293 NotifyFinishedLoad(); |
| 294 } |
| 295 |
| 296 void HiddenWebContents::DidNavigateMainFrame( |
| 297 const content::LoadCommittedDetails& details, |
| 298 const content::FrameNavigateParams& params) { |
| 299 // If the render made a second navigation entry, abort the render. This |
| 300 // avoids having to correctly implement a complex history merging case (this |
| 301 // interacts with location.replace) and correctly synchronize with the |
| 302 // renderer. The final status may be monitored to see we need to revisit this |
| 303 // decision. This does not affect client redirects as those do not push new |
| 304 // history entries. (Calls to location.replace, navigations before onload, and |
| 305 // <meta http-equiv=refresh> with timeouts under 1 second do not create |
| 306 // entries in Blink.) |
| 307 if (web_contents_->GetController().GetEntryCount() > 1) |
| 308 Destroy(); |
| 309 } |
| 310 |
| 311 void HiddenWebContents::DidGetRedirectForResourceRequest( |
| 312 content::RenderFrameHost* render_frame_host, |
| 313 const content::ResourceRedirectDetails& details) { |
| 314 // Redirects are unsupported for hidden renderers. |
| 315 Destroy(); |
| 316 } |
| 317 |
| 318 void HiddenWebContents::Destroy() { |
| 319 if (rendering_has_been_cancelled_) |
| 320 return; |
| 321 |
| 322 rendering_has_been_cancelled_ = true; |
| 323 NotifyFail(); |
| 324 } |
OLD | NEW |