Commit 8b908968 authored by John Chen's avatar John Chen Committed by Commit Bot

[ChromeDriver] Detect stale frame during navigation

While waiting for a pending navigation, verify that the current
frame still exists. Otherwise we might wait forever for a frame
that has already been destroyed.

Bug: chromedriver:3353
Change-Id: I0e80631a3880d24bf207e0871db07f31dad8100b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2129088
Commit-Queue: John Chen <johnchen@chromium.org>
Reviewed-by: default avatarWenbin Zhang <wenbinzhang@google.com>
Cr-Commit-Position: refs/heads/master@{#755133}
parent c4a980d1
...@@ -52,6 +52,21 @@ WebView* FrameTracker::GetTargetForFrame(const std::string& frame_id) { ...@@ -52,6 +52,21 @@ WebView* FrameTracker::GetTargetForFrame(const std::string& frame_id) {
return nullptr; return nullptr;
} }
bool FrameTracker::IsKnownFrame(const std::string& frame_id) const {
if (attached_frames_.count(frame_id) != 0 ||
frame_to_context_map_.count(frame_id) != 0 ||
frame_to_target_map_.count(frame_id) != 0) {
return true;
}
// Frame unknown to this tracker, recursively search all child targets.
for (const auto& it : frame_to_target_map_) {
FrameTracker* child = it.second->GetFrameTracker();
if (child != nullptr && child->IsKnownFrame(frame_id))
return true;
}
return false;
}
void FrameTracker::DeleteTargetForFrame(const std::string& frame_id) { void FrameTracker::DeleteTargetForFrame(const std::string& frame_id) {
frame_to_target_map_.erase(frame_id); frame_to_target_map_.erase(frame_id);
} }
...@@ -59,6 +74,7 @@ void FrameTracker::DeleteTargetForFrame(const std::string& frame_id) { ...@@ -59,6 +74,7 @@ void FrameTracker::DeleteTargetForFrame(const std::string& frame_id) {
Status FrameTracker::OnConnected(DevToolsClient* client) { Status FrameTracker::OnConnected(DevToolsClient* client) {
frame_to_context_map_.clear(); frame_to_context_map_.clear();
frame_to_target_map_.clear(); frame_to_target_map_.clear();
attached_frames_.clear();
// Enable target events to allow tracking iframe targets creation. // Enable target events to allow tracking iframe targets creation.
base::DictionaryValue params; base::DictionaryValue params;
params.SetBoolean("autoAttach", true); params.SetBoolean("autoAttach", true);
...@@ -141,6 +157,18 @@ Status FrameTracker::OnEvent(DevToolsClient* client, ...@@ -141,6 +157,18 @@ Status FrameTracker::OnEvent(DevToolsClient* client,
} }
} else if (method == "Runtime.executionContextsCleared") { } else if (method == "Runtime.executionContextsCleared") {
frame_to_context_map_.clear(); frame_to_context_map_.clear();
} else if (method == "Page.frameAttached") {
std::string frame_id;
if (!params.GetString("frameId", &frame_id))
return Status(kUnknownError,
"missing frameId in Page.frameAttached event");
attached_frames_.insert(frame_id);
} else if (method == "Page.frameDetached") {
std::string frame_id;
if (!params.GetString("frameId", &frame_id))
return Status(kUnknownError,
"missing frameId in Page.frameDetached event");
attached_frames_.erase(frame_id);
} else if (method == "Page.frameNavigated") { } else if (method == "Page.frameNavigated") {
const base::Value* unused_value; const base::Value* unused_value;
if (!params.Get("frame.parentId", &unused_value)) if (!params.Get("frame.parentId", &unused_value))
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <map> #include <map>
#include <string> #include <string>
#include <unordered_set>
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/macros.h" #include "base/macros.h"
...@@ -31,6 +32,7 @@ class FrameTracker : public DevToolsEventListener { ...@@ -31,6 +32,7 @@ class FrameTracker : public DevToolsEventListener {
Status GetContextIdForFrame(const std::string& frame_id, int* context_id); Status GetContextIdForFrame(const std::string& frame_id, int* context_id);
WebView* GetTargetForFrame(const std::string& frame_id); WebView* GetTargetForFrame(const std::string& frame_id);
bool IsKnownFrame(const std::string& frame_id) const;
void DeleteTargetForFrame(const std::string& frame_id); void DeleteTargetForFrame(const std::string& frame_id);
// Overridden from DevToolsEventListener: // Overridden from DevToolsEventListener:
...@@ -42,6 +44,7 @@ class FrameTracker : public DevToolsEventListener { ...@@ -42,6 +44,7 @@ class FrameTracker : public DevToolsEventListener {
private: private:
std::map<std::string, int> frame_to_context_map_; std::map<std::string, int> frame_to_context_map_;
std::map<std::string, std::unique_ptr<WebView>> frame_to_target_map_; std::map<std::string, std::unique_ptr<WebView>> frame_to_target_map_;
std::unordered_set<std::string> attached_frames_;
WebView* web_view_; WebView* web_view_;
DISALLOW_COPY_AND_ASSIGN(FrameTracker); DISALLOW_COPY_AND_ASSIGN(FrameTracker);
......
...@@ -1125,6 +1125,11 @@ void WebViewImpl::ClearNavigationState(const std::string& new_frame_id) { ...@@ -1125,6 +1125,11 @@ void WebViewImpl::ClearNavigationState(const std::string& new_frame_id) {
Status WebViewImpl::IsNotPendingNavigation(const std::string& frame_id, Status WebViewImpl::IsNotPendingNavigation(const std::string& frame_id,
const Timeout* timeout, const Timeout* timeout,
bool* is_not_pending) { bool* is_not_pending) {
if (!frame_id.empty() && !frame_tracker_->IsKnownFrame(frame_id)) {
// Frame has already been destroyed.
*is_not_pending = true;
return Status(kOk);
}
bool is_pending; bool is_pending;
Status status = Status status =
navigation_tracker_->IsPendingNavigation(frame_id, timeout, &is_pending); navigation_tracker_->IsPendingNavigation(frame_id, timeout, &is_pending);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment