Index: trunk/src/content/renderer/render_frame_impl.cc |
=================================================================== |
--- trunk/src/content/renderer/render_frame_impl.cc (revision 254165) |
+++ trunk/src/content/renderer/render_frame_impl.cc (working copy) |
@@ -12,6 +12,8 @@ |
#include "base/debug/dump_without_crashing.h" |
#include "base/i18n/char_iterator.h" |
#include "base/metrics/histogram.h" |
+#include "base/process/kill.h" |
+#include "base/process/process.h" |
#include "base/strings/utf_string_conversions.h" |
#include "base/time/time.h" |
#include "content/child/appcache/appcache_dispatcher.h" |
@@ -49,6 +51,7 @@ |
#include "content/renderer/renderer_webapplicationcachehost_impl.h" |
#include "content/renderer/shared_worker_repository.h" |
#include "content/renderer/websharedworker_proxy.h" |
+#include "net/base/data_url.h" |
#include "net/base/net_errors.h" |
#include "net/http/http_util.h" |
#include "third_party/WebKit/public/platform/WebStorageQuotaCallbacks.h" |
@@ -83,10 +86,12 @@ |
#endif |
using blink::WebContextMenuData; |
+using blink::WebData; |
using blink::WebDataSource; |
using blink::WebDocument; |
using blink::WebFrame; |
using blink::WebHistoryItem; |
+using blink::WebHTTPBody; |
using blink::WebNavigationPolicy; |
using blink::WebPluginParams; |
using blink::WebReferrerPolicy; |
@@ -144,6 +149,67 @@ |
} |
} |
+NOINLINE static void CrashIntentionally() { |
+ // NOTE(shess): Crash directly rather than using NOTREACHED() so |
+ // that the signature is easier to triage in crash reports. |
+ volatile int* zero = NULL; |
+ *zero = 0; |
+} |
+ |
+#if defined(ADDRESS_SANITIZER) |
+NOINLINE static void MaybeTriggerAsanError(const GURL& url) { |
+ // NOTE(rogerm): We intentionally perform an invalid heap access here in |
+ // order to trigger an Address Sanitizer (ASAN) error report. |
+ static const char kCrashDomain[] = "crash"; |
+ static const char kHeapOverflow[] = "/heap-overflow"; |
+ static const char kHeapUnderflow[] = "/heap-underflow"; |
+ static const char kUseAfterFree[] = "/use-after-free"; |
+ static const int kArraySize = 5; |
+ |
+ if (!url.DomainIs(kCrashDomain, sizeof(kCrashDomain) - 1)) |
+ return; |
+ |
+ if (!url.has_path()) |
+ return; |
+ |
+ scoped_ptr<int[]> array(new int[kArraySize]); |
+ std::string crash_type(url.path()); |
+ int dummy = 0; |
+ if (crash_type == kHeapOverflow) { |
+ dummy = array[kArraySize]; |
+ } else if (crash_type == kHeapUnderflow ) { |
+ dummy = array[-1]; |
+ } else if (crash_type == kUseAfterFree) { |
+ int* dangling = array.get(); |
+ array.reset(); |
+ dummy = dangling[kArraySize / 2]; |
+ } |
+ |
+ // Make sure the assignments to the dummy value aren't optimized away. |
+ base::debug::Alias(&dummy); |
+} |
+#endif // ADDRESS_SANITIZER |
+ |
+static void MaybeHandleDebugURL(const GURL& url) { |
+ if (!url.SchemeIs(kChromeUIScheme)) |
+ return; |
+ if (url == GURL(kChromeUICrashURL)) { |
+ CrashIntentionally(); |
+ } else if (url == GURL(kChromeUIKillURL)) { |
+ base::KillProcess(base::GetCurrentProcessHandle(), 1, false); |
+ } else if (url == GURL(kChromeUIHangURL)) { |
+ for (;;) { |
+ base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1)); |
+ } |
+ } else if (url == GURL(kChromeUIShorthangURL)) { |
+ base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(20)); |
+ } |
+ |
+#if defined(ADDRESS_SANITIZER) |
+ MaybeTriggerAsanError(url); |
+#endif // ADDRESS_SANITIZER |
+} |
+ |
} // namespace |
static RenderFrameImpl* (*g_create_render_frame_impl)(RenderViewImpl*, int32) = |
@@ -423,6 +489,7 @@ |
bool handled = true; |
bool msg_is_ok = true; |
IPC_BEGIN_MESSAGE_MAP_EX(RenderFrameImpl, msg, msg_is_ok) |
+ IPC_MESSAGE_HANDLER(FrameMsg_Navigate, OnNavigate) |
IPC_MESSAGE_HANDLER(FrameMsg_SwapOut, OnSwapOut) |
IPC_MESSAGE_HANDLER(FrameMsg_BuffersSwapped, OnBuffersSwapped) |
IPC_MESSAGE_HANDLER_GENERIC(FrameMsg_CompositorFrameSwapped, |
@@ -440,8 +507,190 @@ |
} |
return handled; |
- } |
+} |
+void RenderFrameImpl::OnNavigate(const FrameMsg_Navigate_Params& params) { |
+ MaybeHandleDebugURL(params.url); |
+ if (!render_view_->webview()) |
+ return; |
+ |
+ render_view_->OnNavigate(params); |
+ |
+ bool is_reload = RenderViewImpl::IsReload(params); |
+ WebURLRequest::CachePolicy cache_policy = |
+ WebURLRequest::UseProtocolCachePolicy; |
+ |
+ // If this is a stale back/forward (due to a recent navigation the browser |
+ // didn't know about), ignore it. |
+ if (render_view_->IsBackForwardToStaleEntry(params, is_reload)) |
+ return; |
+ |
+ // Swap this renderer back in if necessary. |
+ if (render_view_->is_swapped_out_) { |
+ // We marked the view as hidden when swapping the view out, so be sure to |
+ // reset the visibility state before navigating to the new URL. |
+ render_view_->webview()->setVisibilityState( |
+ render_view_->visibilityState(), false); |
+ |
+ // If this is an attempt to reload while we are swapped out, we should not |
+ // reload swappedout://, but the previous page, which is stored in |
+ // params.state. Setting is_reload to false will treat this like a back |
+ // navigation to accomplish that. |
+ is_reload = false; |
+ cache_policy = WebURLRequest::ReloadIgnoringCacheData; |
+ |
+ // We refresh timezone when a view is swapped in since timezone |
+ // can get out of sync when the system timezone is updated while |
+ // the view is swapped out. |
+ RenderViewImpl::NotifyTimezoneChange(render_view_->webview()->mainFrame()); |
+ |
+ render_view_->SetSwappedOut(false); |
+ is_swapped_out_ = false; |
+ } |
+ |
+ if (params.should_clear_history_list) { |
+ CHECK_EQ(params.pending_history_list_offset, -1); |
+ CHECK_EQ(params.current_history_list_offset, -1); |
+ CHECK_EQ(params.current_history_list_length, 0); |
+ } |
+ render_view_->history_list_offset_ = params.current_history_list_offset; |
+ render_view_->history_list_length_ = params.current_history_list_length; |
+ if (render_view_->history_list_length_ >= 0) { |
+ render_view_->history_page_ids_.resize( |
+ render_view_->history_list_length_, -1); |
+ } |
+ if (params.pending_history_list_offset >= 0 && |
+ params.pending_history_list_offset < render_view_->history_list_length_) { |
+ render_view_->history_page_ids_[params.pending_history_list_offset] = |
+ params.page_id; |
+ } |
+ |
+ GetContentClient()->SetActiveURL(params.url); |
+ |
+ WebFrame* frame = frame_; |
+ if (!params.frame_to_navigate.empty()) { |
+ // TODO(nasko): Move this lookup to the browser process. |
+ frame = render_view_->webview()->findFrameByName( |
+ WebString::fromUTF8(params.frame_to_navigate)); |
+ CHECK(frame) << "Invalid frame name passed: " << params.frame_to_navigate; |
+ } |
+ |
+ if (is_reload && frame->currentHistoryItem().isNull()) { |
+ // We cannot reload if we do not have any history state. This happens, for |
+ // example, when recovering from a crash. |
+ is_reload = false; |
+ cache_policy = WebURLRequest::ReloadIgnoringCacheData; |
+ } |
+ |
+ render_view_->pending_navigation_params_.reset( |
+ new FrameMsg_Navigate_Params(params)); |
+ |
+ // If we are reloading, then WebKit will use the history state of the current |
+ // page, so we should just ignore any given history state. Otherwise, if we |
+ // have history state, then we need to navigate to it, which corresponds to a |
+ // back/forward navigation event. |
+ if (is_reload) { |
+ bool reload_original_url = |
+ (params.navigation_type == |
+ FrameMsg_Navigate_Type::RELOAD_ORIGINAL_REQUEST_URL); |
+ bool ignore_cache = (params.navigation_type == |
+ FrameMsg_Navigate_Type::RELOAD_IGNORING_CACHE); |
+ |
+ if (reload_original_url) |
+ frame->reloadWithOverrideURL(params.url, true); |
+ else |
+ frame->reload(ignore_cache); |
+ } else if (params.page_state.IsValid()) { |
+ // We must know the page ID of the page we are navigating back to. |
+ DCHECK_NE(params.page_id, -1); |
+ WebHistoryItem item = PageStateToHistoryItem(params.page_state); |
+ if (!item.isNull()) { |
+ // Ensure we didn't save the swapped out URL in UpdateState, since the |
+ // browser should never be telling us to navigate to swappedout://. |
+ CHECK(item.urlString() != WebString::fromUTF8(kSwappedOutURL)); |
+ frame->loadHistoryItem(item, cache_policy); |
+ } |
+ } else if (!params.base_url_for_data_url.is_empty()) { |
+ // A loadData request with a specified base URL. |
+ std::string mime_type, charset, data; |
+ if (net::DataURL::Parse(params.url, &mime_type, &charset, &data)) { |
+ frame->loadData( |
+ WebData(data.c_str(), data.length()), |
+ WebString::fromUTF8(mime_type), |
+ WebString::fromUTF8(charset), |
+ params.base_url_for_data_url, |
+ params.history_url_for_data_url, |
+ false); |
+ } else { |
+ CHECK(false) << |
+ "Invalid URL passed: " << params.url.possibly_invalid_spec(); |
+ } |
+ } else { |
+ // Navigate to the given URL. |
+ WebURLRequest request(params.url); |
+ |
+ // A session history navigation should have been accompanied by state. |
+ CHECK_EQ(params.page_id, -1); |
+ |
+ if (frame->isViewSourceModeEnabled()) |
+ request.setCachePolicy(WebURLRequest::ReturnCacheDataElseLoad); |
+ |
+ if (params.referrer.url.is_valid()) { |
+ WebString referrer = WebSecurityPolicy::generateReferrerHeader( |
+ params.referrer.policy, |
+ params.url, |
+ WebString::fromUTF8(params.referrer.url.spec())); |
+ if (!referrer.isEmpty()) |
+ request.setHTTPReferrer(referrer, params.referrer.policy); |
+ } |
+ |
+ if (!params.extra_headers.empty()) { |
+ for (net::HttpUtil::HeadersIterator i(params.extra_headers.begin(), |
+ params.extra_headers.end(), "\n"); |
+ i.GetNext(); ) { |
+ request.addHTTPHeaderField(WebString::fromUTF8(i.name()), |
+ WebString::fromUTF8(i.values())); |
+ } |
+ } |
+ |
+ if (params.is_post) { |
+ request.setHTTPMethod(WebString::fromUTF8("POST")); |
+ |
+ // Set post data. |
+ WebHTTPBody http_body; |
+ http_body.initialize(); |
+ const char* data = NULL; |
+ if (params.browser_initiated_post_data.size()) { |
+ data = reinterpret_cast<const char*>( |
+ ¶ms.browser_initiated_post_data.front()); |
+ } |
+ http_body.appendData( |
+ WebData(data, params.browser_initiated_post_data.size())); |
+ request.setHTTPBody(http_body); |
+ } |
+ |
+ frame->loadRequest(request); |
+ |
+ // If this is a cross-process navigation, the browser process will send |
+ // along the proper navigation start value. |
+ if (!params.browser_navigation_start.is_null() && |
+ frame->provisionalDataSource()) { |
+ // browser_navigation_start is likely before this process existed, so we |
+ // can't use InterProcessTimeTicksConverter. Instead, the best we can do |
+ // is just ensure we don't report a bogus value in the future. |
+ base::TimeTicks navigation_start = std::min( |
+ base::TimeTicks::Now(), params.browser_navigation_start); |
+ double navigation_start_seconds = |
+ (navigation_start - base::TimeTicks()).InSecondsF(); |
+ frame->provisionalDataSource()->setNavigationStartTime( |
+ navigation_start_seconds); |
+ } |
+ } |
+ |
+ // In case LoadRequest failed before DidCreateDataSource was called. |
+ render_view_->pending_navigation_params_.reset(); |
+} |
+ |
void RenderFrameImpl::OnSwapOut() { |
// Only run unload if we're not swapped out yet, but send the ack either way. |
if (!is_swapped_out_) { |
@@ -1089,7 +1338,8 @@ |
// If we failed on a browser initiated request, then make sure that our error |
// page load is regarded as the same browser initiated request. |
if (!navigation_state->is_content_initiated()) { |
- render_view_->pending_navigation_params_.reset(new ViewMsg_Navigate_Params); |
+ render_view_->pending_navigation_params_.reset( |
+ new FrameMsg_Navigate_Params); |
render_view_->pending_navigation_params_->page_id = |
navigation_state->pending_page_id(); |
render_view_->pending_navigation_params_->pending_history_list_offset = |