| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 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 "components/html_viewer/html_frame_tree_manager.h" | |
| 6 | |
| 7 #include <stddef.h> | |
| 8 #include <algorithm> | |
| 9 #include <utility> | |
| 10 | |
| 11 #include "base/command_line.h" | |
| 12 #include "base/logging.h" | |
| 13 #include "base/macros.h" | |
| 14 #include "components/html_viewer/blink_basic_type_converters.h" | |
| 15 #include "components/html_viewer/blink_url_request_type_converters.h" | |
| 16 #include "components/html_viewer/document_resource_waiter.h" | |
| 17 #include "components/html_viewer/global_state.h" | |
| 18 #include "components/html_viewer/html_factory.h" | |
| 19 #include "components/html_viewer/html_frame.h" | |
| 20 #include "components/html_viewer/html_frame_delegate.h" | |
| 21 #include "components/html_viewer/html_frame_tree_manager_observer.h" | |
| 22 #include "components/mus/public/cpp/window_tree_connection.h" | |
| 23 #include "components/web_view/web_view_switches.h" | |
| 24 #include "third_party/WebKit/public/web/WebLocalFrame.h" | |
| 25 #include "third_party/WebKit/public/web/WebRemoteFrame.h" | |
| 26 #include "third_party/WebKit/public/web/WebTreeScopeType.h" | |
| 27 #include "third_party/WebKit/public/web/WebView.h" | |
| 28 #include "ui/gfx/geometry/dip_util.h" | |
| 29 #include "ui/gfx/geometry/size.h" | |
| 30 | |
| 31 namespace html_viewer { | |
| 32 namespace { | |
| 33 | |
| 34 // Returns the index of the FrameData with the id of |frame_id| in |index|. On | |
| 35 // success returns true, otherwise false. | |
| 36 bool FindFrameDataIndex( | |
| 37 const mojo::Array<web_view::mojom::FrameDataPtr>& frame_data, | |
| 38 uint32_t frame_id, | |
| 39 size_t* index) { | |
| 40 for (size_t i = 0; i < frame_data.size(); ++i) { | |
| 41 if (frame_data[i]->frame_id == frame_id) { | |
| 42 *index = i; | |
| 43 return true; | |
| 44 } | |
| 45 } | |
| 46 return false; | |
| 47 } | |
| 48 | |
| 49 } // namespace | |
| 50 | |
| 51 // Object that calls OnHTMLFrameTreeManagerChangeIdAdvanced() from the | |
| 52 // destructor. | |
| 53 class HTMLFrameTreeManager::ChangeIdAdvancedNotifier { | |
| 54 public: | |
| 55 explicit ChangeIdAdvancedNotifier( | |
| 56 base::ObserverList<HTMLFrameTreeManagerObserver>* observers) | |
| 57 : observers_(observers) {} | |
| 58 ~ChangeIdAdvancedNotifier() { | |
| 59 FOR_EACH_OBSERVER(HTMLFrameTreeManagerObserver, *observers_, | |
| 60 OnHTMLFrameTreeManagerChangeIdAdvanced()); | |
| 61 } | |
| 62 | |
| 63 private: | |
| 64 base::ObserverList<HTMLFrameTreeManagerObserver>* observers_; | |
| 65 | |
| 66 DISALLOW_COPY_AND_ASSIGN(ChangeIdAdvancedNotifier); | |
| 67 }; | |
| 68 | |
| 69 // static | |
| 70 HTMLFrameTreeManager::TreeMap* HTMLFrameTreeManager::instances_ = nullptr; | |
| 71 | |
| 72 // static | |
| 73 HTMLFrame* HTMLFrameTreeManager::CreateFrameAndAttachToTree( | |
| 74 GlobalState* global_state, | |
| 75 mus::Window* window, | |
| 76 scoped_ptr<DocumentResourceWaiter> resource_waiter, | |
| 77 HTMLFrameDelegate* delegate) { | |
| 78 if (!instances_) | |
| 79 instances_ = new TreeMap; | |
| 80 | |
| 81 mojo::InterfaceRequest<web_view::mojom::FrameClient> frame_client_request; | |
| 82 web_view::mojom::FramePtr server_frame; | |
| 83 mojo::Array<web_view::mojom::FrameDataPtr> frame_data; | |
| 84 uint32_t change_id; | |
| 85 uint32_t window_id; | |
| 86 web_view::mojom::WindowConnectType window_connect_type; | |
| 87 web_view::mojom::FrameClient::OnConnectCallback on_connect_callback; | |
| 88 resource_waiter->Release(&frame_client_request, &server_frame, &frame_data, | |
| 89 &change_id, &window_id, &window_connect_type, | |
| 90 &on_connect_callback); | |
| 91 resource_waiter.reset(); | |
| 92 | |
| 93 on_connect_callback.Run(); | |
| 94 | |
| 95 HTMLFrameTreeManager* frame_tree = | |
| 96 FindFrameTreeWithRoot(frame_data[0]->frame_id); | |
| 97 | |
| 98 DCHECK(!frame_tree || change_id <= frame_tree->change_id_); | |
| 99 | |
| 100 DVLOG(2) << "HTMLFrameTreeManager::CreateFrameAndAttachToTree " | |
| 101 << " frame_tree=" << frame_tree << " use_existing=" | |
| 102 << (window_connect_type == | |
| 103 web_view::mojom::WindowConnectType::USE_EXISTING) | |
| 104 << " frame_id=" << window_id; | |
| 105 if (window_connect_type == web_view::mojom::WindowConnectType::USE_EXISTING && | |
| 106 !frame_tree) { | |
| 107 DVLOG(1) << "was told to use existing window but do not have frame tree"; | |
| 108 return nullptr; | |
| 109 } | |
| 110 | |
| 111 if (!frame_tree) { | |
| 112 frame_tree = new HTMLFrameTreeManager(global_state); | |
| 113 frame_tree->Init(delegate, window, frame_data, change_id); | |
| 114 (*instances_)[frame_data[0]->frame_id] = frame_tree; | |
| 115 } else if (window_connect_type == | |
| 116 web_view::mojom::WindowConnectType::USE_EXISTING) { | |
| 117 HTMLFrame* existing_frame = frame_tree->root_->FindFrame(window_id); | |
| 118 if (!existing_frame) { | |
| 119 DVLOG(1) << "was told to use existing window but could not find window"; | |
| 120 return nullptr; | |
| 121 } | |
| 122 if (!existing_frame->IsLocal()) { | |
| 123 DVLOG(1) << "was told to use existing window, but frame is remote"; | |
| 124 return nullptr; | |
| 125 } | |
| 126 existing_frame->SwapDelegate(delegate); | |
| 127 } else { | |
| 128 // We're going to share a frame tree. We should know about the frame. | |
| 129 CHECK(window->id() != frame_data[0]->frame_id); | |
| 130 HTMLFrame* existing_frame = frame_tree->root_->FindFrame(window->id()); | |
| 131 if (existing_frame) { | |
| 132 CHECK(!existing_frame->IsLocal()); | |
| 133 size_t frame_data_index = 0u; | |
| 134 CHECK(FindFrameDataIndex(frame_data, window->id(), &frame_data_index)); | |
| 135 const web_view::mojom::FrameDataPtr& data = frame_data[frame_data_index]; | |
| 136 existing_frame->SwapToLocal(delegate, window, data->client_properties); | |
| 137 } else { | |
| 138 // If we can't find the frame and the change_id of the incoming | |
| 139 // tree is before the change id we've processed, then we removed the | |
| 140 // frame and need do nothing. | |
| 141 if (change_id < frame_tree->change_id_) | |
| 142 return nullptr; | |
| 143 | |
| 144 // We removed the frame but it hasn't been acked yet. | |
| 145 if (frame_tree->pending_remove_ids_.count(window->id())) | |
| 146 return nullptr; | |
| 147 | |
| 148 // We don't know about the frame, but should. Something is wrong. | |
| 149 DVLOG(1) << "unable to locate frame to attach to"; | |
| 150 return nullptr; | |
| 151 } | |
| 152 } | |
| 153 | |
| 154 HTMLFrame* frame = frame_tree->root_->FindFrame(window_id); | |
| 155 DCHECK(frame); | |
| 156 frame->Bind(std::move(server_frame), std::move(frame_client_request)); | |
| 157 return frame; | |
| 158 } | |
| 159 | |
| 160 // static | |
| 161 HTMLFrameTreeManager* HTMLFrameTreeManager::FindFrameTreeWithRoot( | |
| 162 uint32_t root_frame_id) { | |
| 163 return (!base::CommandLine::ForCurrentProcess()->HasSwitch( | |
| 164 web_view::switches::kOOPIFAlwaysCreateNewFrameTree) && | |
| 165 instances_ && instances_->count(root_frame_id)) | |
| 166 ? (*instances_)[root_frame_id] | |
| 167 : nullptr; | |
| 168 } | |
| 169 | |
| 170 blink::WebView* HTMLFrameTreeManager::GetWebView() { | |
| 171 return root_->web_view(); | |
| 172 } | |
| 173 | |
| 174 void HTMLFrameTreeManager::AddObserver(HTMLFrameTreeManagerObserver* observer) { | |
| 175 observers_.AddObserver(observer); | |
| 176 } | |
| 177 | |
| 178 void HTMLFrameTreeManager::RemoveObserver( | |
| 179 HTMLFrameTreeManagerObserver* observer) { | |
| 180 observers_.RemoveObserver(observer); | |
| 181 } | |
| 182 | |
| 183 void HTMLFrameTreeManager::OnFrameDestroyed(HTMLFrame* frame) { | |
| 184 if (!in_process_on_frame_removed_) | |
| 185 pending_remove_ids_.insert(frame->id()); | |
| 186 | |
| 187 if (frame == root_) { | |
| 188 // If |root_| is removed before |local_frame_|, we don't have a way to | |
| 189 // select a new local frame when |local_frame_| is removed. On the other | |
| 190 // hand, it is impossible to remove |root_| after all local frames are gone, | |
| 191 // because by that time this object has already been deleted. | |
| 192 CHECK_EQ(root_, local_frame_); | |
| 193 root_ = nullptr; | |
| 194 } | |
| 195 | |
| 196 if (frame != local_frame_) | |
| 197 return; | |
| 198 | |
| 199 // |local_frame_| is being removed. We need to find out whether we have local | |
| 200 // frames that are not descendants of |local_frame_|, and if yes select a new | |
| 201 // |local_frame_|. | |
| 202 local_frame_ = FindNewLocalFrame(); | |
| 203 if (!local_frame_) | |
| 204 delete this; | |
| 205 } | |
| 206 | |
| 207 HTMLFrameTreeManager::HTMLFrameTreeManager(GlobalState* global_state) | |
| 208 : global_state_(global_state), | |
| 209 root_(nullptr), | |
| 210 local_frame_(nullptr), | |
| 211 change_id_(0u), | |
| 212 in_process_on_frame_removed_(false), | |
| 213 weak_factory_(this) {} | |
| 214 | |
| 215 HTMLFrameTreeManager::~HTMLFrameTreeManager() { | |
| 216 DCHECK(!local_frame_); | |
| 217 RemoveFromInstances(); | |
| 218 | |
| 219 FOR_EACH_OBSERVER(HTMLFrameTreeManagerObserver, observers_, | |
| 220 OnHTMLFrameTreeManagerDestroyed()); | |
| 221 } | |
| 222 | |
| 223 void HTMLFrameTreeManager::Init( | |
| 224 HTMLFrameDelegate* delegate, | |
| 225 mus::Window* local_window, | |
| 226 const mojo::Array<web_view::mojom::FrameDataPtr>& frame_data, | |
| 227 uint32_t change_id) { | |
| 228 change_id_ = change_id; | |
| 229 root_ = | |
| 230 BuildFrameTree(delegate, frame_data, local_window->id(), local_window); | |
| 231 local_frame_ = root_->FindFrame(local_window->id()); | |
| 232 CHECK(local_frame_); | |
| 233 local_frame_->UpdateFocus(); | |
| 234 } | |
| 235 | |
| 236 HTMLFrame* HTMLFrameTreeManager::BuildFrameTree( | |
| 237 HTMLFrameDelegate* delegate, | |
| 238 const mojo::Array<web_view::mojom::FrameDataPtr>& frame_data, | |
| 239 uint32_t local_frame_id, | |
| 240 mus::Window* local_window) { | |
| 241 std::vector<HTMLFrame*> parents; | |
| 242 HTMLFrame* root = nullptr; | |
| 243 HTMLFrame* last_frame = nullptr; | |
| 244 for (size_t i = 0; i < frame_data.size(); ++i) { | |
| 245 if (last_frame && frame_data[i]->parent_id == last_frame->id()) { | |
| 246 parents.push_back(last_frame); | |
| 247 } else if (!parents.empty()) { | |
| 248 while (parents.back()->id() != frame_data[i]->parent_id) | |
| 249 parents.pop_back(); | |
| 250 } | |
| 251 HTMLFrame::CreateParams params(this, | |
| 252 !parents.empty() ? parents.back() : nullptr, | |
| 253 frame_data[i]->frame_id, local_window, | |
| 254 frame_data[i]->client_properties, nullptr); | |
| 255 if (frame_data[i]->frame_id == local_frame_id) | |
| 256 params.delegate = delegate; | |
| 257 | |
| 258 HTMLFrame* frame = delegate->GetHTMLFactory()->CreateHTMLFrame(¶ms); | |
| 259 if (!last_frame) | |
| 260 root = frame; | |
| 261 else | |
| 262 DCHECK(frame->parent()); | |
| 263 last_frame = frame; | |
| 264 } | |
| 265 return root; | |
| 266 } | |
| 267 | |
| 268 void HTMLFrameTreeManager::RemoveFromInstances() { | |
| 269 for (auto pair : *instances_) { | |
| 270 if (pair.second == this) { | |
| 271 instances_->erase(pair.first); | |
| 272 return; | |
| 273 } | |
| 274 } | |
| 275 } | |
| 276 | |
| 277 bool HTMLFrameTreeManager::PrepareForStructureChange(HTMLFrame* source, | |
| 278 uint32_t change_id) { | |
| 279 // The change ids may differ if multiple HTMLDocuments are attached to the | |
| 280 // same tree (which means we have multiple FrameClient for the same tree). | |
| 281 if (change_id != (change_id_ + 1)) | |
| 282 return false; | |
| 283 | |
| 284 // We only process changes for the topmost local root. | |
| 285 if (source != local_frame_) | |
| 286 return false; | |
| 287 | |
| 288 // Update the id as the change is going to be applied (or we can assume it | |
| 289 // will be applied if we get here). | |
| 290 change_id_ = change_id; | |
| 291 return true; | |
| 292 } | |
| 293 | |
| 294 void HTMLFrameTreeManager::ProcessOnFrameAdded( | |
| 295 HTMLFrame* source, | |
| 296 uint32_t change_id, | |
| 297 web_view::mojom::FrameDataPtr frame_data) { | |
| 298 if (!PrepareForStructureChange(source, change_id)) | |
| 299 return; | |
| 300 | |
| 301 ChangeIdAdvancedNotifier notifier(&observers_); | |
| 302 | |
| 303 HTMLFrame* parent = root_->FindFrame(frame_data->parent_id); | |
| 304 if (!parent) { | |
| 305 DVLOG(1) << "Received invalid parent in OnFrameAdded " | |
| 306 << frame_data->parent_id; | |
| 307 return; | |
| 308 } | |
| 309 if (root_->FindFrame(frame_data->frame_id)) { | |
| 310 DVLOG(1) << "Child with id already exists in OnFrameAdded " | |
| 311 << frame_data->frame_id; | |
| 312 return; | |
| 313 } | |
| 314 | |
| 315 // Because notification is async it's entirely possible for us to create a | |
| 316 // new frame, and remove it before we get the add from the server. This check | |
| 317 // ensures we don't add back a frame we explicitly removed. | |
| 318 if (pending_remove_ids_.count(frame_data->frame_id)) | |
| 319 return; | |
| 320 | |
| 321 DVLOG(2) << "OnFrameAdded this=" << this | |
| 322 << " frame_id=" << frame_data->frame_id; | |
| 323 | |
| 324 HTMLFrame::CreateParams params(this, parent, frame_data->frame_id, nullptr, | |
| 325 frame_data->client_properties, nullptr); | |
| 326 // |parent| takes ownership of created HTMLFrame. | |
| 327 source->GetFirstAncestorWithDelegate() | |
| 328 ->delegate_->GetHTMLFactory() | |
| 329 ->CreateHTMLFrame(¶ms); | |
| 330 } | |
| 331 | |
| 332 void HTMLFrameTreeManager::ProcessOnFrameRemoved(HTMLFrame* source, | |
| 333 uint32_t change_id, | |
| 334 uint32_t frame_id) { | |
| 335 if (!PrepareForStructureChange(source, change_id)) | |
| 336 return; | |
| 337 | |
| 338 ChangeIdAdvancedNotifier notifier(&observers_); | |
| 339 | |
| 340 pending_remove_ids_.erase(frame_id); | |
| 341 | |
| 342 HTMLFrame* frame = root_->FindFrame(frame_id); | |
| 343 if (!frame) { | |
| 344 DVLOG(1) << "OnFrameRemoved with unknown frame " << frame_id; | |
| 345 return; | |
| 346 } | |
| 347 | |
| 348 // We shouldn't see requests to remove the root. | |
| 349 if (frame == root_) { | |
| 350 DVLOG(1) << "OnFrameRemoved supplied root; ignoring"; | |
| 351 return; | |
| 352 } | |
| 353 | |
| 354 // Requests to remove local frames are followed by the Window being destroyed. | |
| 355 // We handle destruction there. | |
| 356 if (frame->IsLocal()) | |
| 357 return; | |
| 358 | |
| 359 DVLOG(2) << "OnFrameRemoved this=" << this << " frame_id=" << frame_id; | |
| 360 | |
| 361 DCHECK(!in_process_on_frame_removed_); | |
| 362 in_process_on_frame_removed_ = true; | |
| 363 base::WeakPtr<HTMLFrameTreeManager> ref(weak_factory_.GetWeakPtr()); | |
| 364 frame->Close(); | |
| 365 if (!ref) | |
| 366 return; // We were deleted. | |
| 367 | |
| 368 in_process_on_frame_removed_ = false; | |
| 369 } | |
| 370 | |
| 371 void HTMLFrameTreeManager::ProcessOnFrameClientPropertyChanged( | |
| 372 HTMLFrame* source, | |
| 373 uint32_t frame_id, | |
| 374 const mojo::String& name, | |
| 375 mojo::Array<uint8_t> new_data) { | |
| 376 if (source != local_frame_) | |
| 377 return; | |
| 378 | |
| 379 HTMLFrame* frame = root_->FindFrame(frame_id); | |
| 380 if (frame) | |
| 381 frame->SetValueFromClientProperty(name, std::move(new_data)); | |
| 382 } | |
| 383 | |
| 384 HTMLFrame* HTMLFrameTreeManager::FindNewLocalFrame() { | |
| 385 HTMLFrame* new_local_frame = nullptr; | |
| 386 | |
| 387 if (root_) { | |
| 388 std::queue<HTMLFrame*> nodes; | |
| 389 nodes.push(root_); | |
| 390 | |
| 391 while (!nodes.empty()) { | |
| 392 HTMLFrame* node = nodes.front(); | |
| 393 nodes.pop(); | |
| 394 | |
| 395 if (node == local_frame_) | |
| 396 continue; | |
| 397 | |
| 398 if (node->IsLocal()) { | |
| 399 new_local_frame = node; | |
| 400 break; | |
| 401 } | |
| 402 | |
| 403 for (const auto& child : node->children()) | |
| 404 nodes.push(child); | |
| 405 } | |
| 406 } | |
| 407 | |
| 408 return new_local_frame; | |
| 409 } | |
| 410 | |
| 411 } // namespace mojo | |
| OLD | NEW |