Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(191)

Side by Side Diff: components/web_view/frame.cc

Issue 1677293002: Bye bye Mandoline (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: moar Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « components/web_view/frame.h ('k') | components/web_view/frame_apptest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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/web_view/frame.h"
6
7 #include <stddef.h>
8 #include <algorithm>
9 #include <utility>
10
11 #include "base/auto_reset.h"
12 #include "base/bind.h"
13 #include "base/callback.h"
14 #include "base/stl_util.h"
15 #include "base/trace_event/trace_event.h"
16 #include "components/mus/public/cpp/window.h"
17 #include "components/mus/public/cpp/window_property.h"
18 #include "components/web_view/frame_tree.h"
19 #include "components/web_view/frame_tree_delegate.h"
20 #include "components/web_view/frame_user_data.h"
21 #include "components/web_view/frame_utils.h"
22 #include "mojo/common/url_type_converters.h"
23 #include "mojo/shell/public/interfaces/shell.mojom.h"
24 #include "url/gurl.h"
25
26 using mus::Window;
27
28 MUS_DECLARE_WINDOW_PROPERTY_TYPE(web_view::Frame*);
29
30 namespace web_view {
31
32 // Used to find the Frame associated with a Window.
33 MUS_DEFINE_LOCAL_WINDOW_PROPERTY_KEY(Frame*, kFrame, nullptr);
34
35 namespace {
36
37 const uint32_t kNoParentId = 0u;
38 const mus::ConnectionSpecificId kInvalidConnectionId = 0u;
39
40 mojom::FrameDataPtr FrameToFrameData(const Frame* frame) {
41 mojom::FrameDataPtr frame_data(mojom::FrameData::New());
42 frame_data->frame_id = frame->id();
43 frame_data->parent_id = frame->parent() ? frame->parent()->id() : kNoParentId;
44 frame_data->client_properties =
45 mojo::Map<mojo::String, mojo::Array<uint8_t>>::From(
46 frame->client_properties());
47 return frame_data;
48 }
49
50 } // namespace
51
52 struct Frame::FrameUserDataAndBinding {
53 scoped_ptr<FrameUserData> user_data;
54 scoped_ptr<mojo::Binding<mojom::Frame>> frame_binding;
55 };
56
57 Frame::Frame(FrameTree* tree,
58 Window* window,
59 uint32_t frame_id,
60 uint32_t app_id,
61 WindowOwnership window_ownership,
62 mojom::FrameClient* frame_client,
63 scoped_ptr<FrameUserData> user_data,
64 const ClientPropertyMap& client_properties)
65 : tree_(tree),
66 window_(nullptr),
67 embedded_connection_id_(kInvalidConnectionId),
68 id_(frame_id),
69 app_id_(app_id),
70 parent_(nullptr),
71 window_ownership_(window_ownership),
72 user_data_(std::move(user_data)),
73 frame_client_(frame_client),
74 loading_(false),
75 progress_(0.f),
76 client_properties_(client_properties),
77 waiting_for_on_will_navigate_ack_(false),
78 embed_weak_ptr_factory_(this),
79 navigate_weak_ptr_factory_(this) {
80 if (window)
81 SetWindow(window);
82 }
83
84 Frame::~Frame() {
85 DVLOG(2) << "~Frame id=" << id_ << " this=" << this;
86 if (window_)
87 window_->RemoveObserver(this);
88 while (!children_.empty())
89 delete children_[0];
90 if (parent_)
91 parent_->Remove(this);
92 if (window_) {
93 window_->ClearLocalProperty(kFrame);
94 if (window_ownership_ == WindowOwnership::OWNS_WINDOW)
95 window_->Destroy();
96 }
97 tree_->delegate_->DidDestroyFrame(this);
98 }
99
100 void Frame::Init(Frame* parent,
101 mus::mojom::WindowTreeClientPtr window_tree_client,
102 mojo::InterfaceRequest<mojom::Frame> frame_request,
103 base::TimeTicks navigation_start_time) {
104 {
105 // Set the FrameClient to null so that we don't notify the client of the
106 // add before OnConnect().
107 base::AutoReset<mojom::FrameClient*> frame_client_resetter(&frame_client_,
108 nullptr);
109 if (parent)
110 parent->Add(this);
111 }
112
113 const ClientType client_type = frame_request.is_pending()
114 ? ClientType::NEW_CHILD_FRAME
115 : ClientType::EXISTING_FRAME_NEW_APP;
116 InitClient(client_type, nullptr, std::move(window_tree_client),
117 std::move(frame_request), navigation_start_time);
118
119 tree_->delegate_->DidCreateFrame(this);
120
121 DVLOG(2) << "Frame id=" << id_ << " parent=" << (parent_ ? parent_->id_ : 0)
122 << " app_id=" << app_id_ << " this=" << this;
123 }
124
125 // static
126 Frame* Frame::FindFirstFrameAncestor(Window* window) {
127 while (window && !window->GetLocalProperty(kFrame))
128 window = window->parent();
129 return window ? window->GetLocalProperty(kFrame) : nullptr;
130 }
131
132 const Frame* Frame::FindFrame(uint32_t id) const {
133 if (id == id_)
134 return this;
135
136 for (const Frame* child : children_) {
137 const Frame* match = child->FindFrame(id);
138 if (match)
139 return match;
140 }
141 return nullptr;
142 }
143
144 bool Frame::HasAncestor(const Frame* frame) const {
145 const Frame* current = this;
146 while (current && current != frame)
147 current = current->parent_;
148 return current == frame;
149 }
150
151 bool Frame::IsLoading() const {
152 if (loading_)
153 return true;
154 for (const Frame* child : children_) {
155 if (child->IsLoading())
156 return true;
157 }
158 return false;
159 }
160
161 double Frame::GatherProgress(int* frame_count) const {
162 ++(*frame_count);
163 double progress = progress_;
164 for (const Frame* child : children_)
165 progress += child->GatherProgress(frame_count);
166 return progress_;
167 }
168
169 void Frame::Find(int32_t request_id,
170 const mojo::String& search_text,
171 mojom::FindOptionsPtr options,
172 bool wrap_within_frame,
173 const FindCallback& callback) {
174 frame_client_->Find(request_id, search_text, std::move(options),
175 wrap_within_frame, callback);
176 }
177
178 void Frame::StopFinding(bool clear_selection) {
179 frame_client_->StopFinding(clear_selection);
180 }
181
182 void Frame::HighlightFindResults(int32_t request_id,
183 const mojo::String& search_text,
184 mojom::FindOptionsPtr options,
185 bool reset) {
186 frame_client_->HighlightFindResults(request_id, search_text,
187 std::move(options), reset);
188 }
189
190 void Frame::StopHighlightingFindResults() {
191 frame_client_->StopHighlightingFindResults();
192 }
193
194 void Frame::InitClient(ClientType client_type,
195 scoped_ptr<FrameUserDataAndBinding> data_and_binding,
196 mus::mojom::WindowTreeClientPtr window_tree_client,
197 mojo::InterfaceRequest<mojom::Frame> frame_request,
198 base::TimeTicks navigation_start_time) {
199 if (client_type == ClientType::EXISTING_FRAME_NEW_APP && window_tree_client) {
200 embedded_connection_id_ = kInvalidConnectionId;
201 embed_weak_ptr_factory_.InvalidateWeakPtrs();
202 window_->Embed(
203 std::move(window_tree_client),
204 mus::mojom::WindowTree::kAccessPolicyDefault,
205 base::Bind(&Frame::OnEmbedAck, embed_weak_ptr_factory_.GetWeakPtr()));
206 }
207
208 if (client_type == ClientType::NEW_CHILD_FRAME) {
209 // Don't install an error handler. We allow for the target to only
210 // implement WindowTreeClient.
211 // This frame (and client) was created by an existing FrameClient. There
212 // is no need to send it OnConnect().
213 frame_binding_.reset(
214 new mojo::Binding<mojom::Frame>(this, std::move(frame_request)));
215 frame_client_->OnConnect(
216 nullptr, tree_->change_id(), id_, mojom::WindowConnectType::USE_NEW,
217 mojo::Array<mojom::FrameDataPtr>(),
218 navigation_start_time.ToInternalValue(),
219 base::Bind(&OnConnectAck, base::Passed(&data_and_binding)));
220 } else {
221 std::vector<const Frame*> frames;
222 tree_->root()->BuildFrameTree(&frames);
223
224 mojo::Array<mojom::FrameDataPtr> array(frames.size());
225 for (size_t i = 0; i < frames.size(); ++i)
226 array[i] = FrameToFrameData(frames[i]);
227
228 mojom::FramePtr frame_ptr;
229 // Don't install an error handler. We allow for the target to only
230 // implement WindowTreeClient.
231 frame_binding_.reset(
232 new mojo::Binding<mojom::Frame>(this, GetProxy(&frame_ptr)));
233 frame_client_->OnConnect(
234 std::move(frame_ptr), tree_->change_id(), id_,
235 client_type == ClientType::EXISTING_FRAME_SAME_APP
236 ? mojom::WindowConnectType::USE_EXISTING
237 : mojom::WindowConnectType::USE_NEW,
238 std::move(array), navigation_start_time.ToInternalValue(),
239 base::Bind(&OnConnectAck, base::Passed(&data_and_binding)));
240 tree_->delegate_->DidStartNavigation(this);
241
242 // We need |embedded_connection_id_| is order to validate requests to
243 // create a child frame (OnCreatedFrame()). Pause incoming methods until
244 // we get the id to prevent race conditions.
245 if (embedded_connection_id_ == kInvalidConnectionId)
246 frame_binding_->PauseIncomingMethodCallProcessing();
247 }
248 }
249
250 // static
251 void Frame::OnConnectAck(scoped_ptr<FrameUserDataAndBinding> data_and_binding) {
252 }
253
254 void Frame::ChangeClient(mojom::FrameClient* frame_client,
255 scoped_ptr<FrameUserData> user_data,
256 mus::mojom::WindowTreeClientPtr window_tree_client,
257 uint32_t app_id,
258 base::TimeTicks navigation_start_time) {
259 while (!children_.empty())
260 delete children_[0];
261
262 ClientType client_type = !window_tree_client
263 ? ClientType::EXISTING_FRAME_SAME_APP
264 : ClientType::EXISTING_FRAME_NEW_APP;
265 scoped_ptr<FrameUserDataAndBinding> data_and_binding;
266
267 if (client_type == ClientType::EXISTING_FRAME_SAME_APP) {
268 // See comment in InitClient() for details.
269 data_and_binding.reset(new FrameUserDataAndBinding);
270 data_and_binding->user_data = std::move(user_data_);
271 data_and_binding->frame_binding = std::move(frame_binding_);
272 } else {
273 loading_ = false;
274 progress_ = 0.f;
275 }
276
277 user_data_ = std::move(user_data);
278 frame_client_ = frame_client;
279 frame_binding_.reset();
280 app_id_ = app_id;
281
282 InitClient(client_type, std::move(data_and_binding),
283 std::move(window_tree_client), nullptr, navigation_start_time);
284 }
285
286 void Frame::OnEmbedAck(bool success, mus::ConnectionSpecificId connection_id) {
287 if (success)
288 embedded_connection_id_ = connection_id;
289 if (frame_binding_->is_bound())
290 frame_binding_->ResumeIncomingMethodCallProcessing();
291 }
292
293 void Frame::OnWillNavigateAck(
294 mojom::FrameClient* frame_client,
295 scoped_ptr<FrameUserData> user_data,
296 mus::mojom::WindowTreeClientPtr window_tree_client,
297 uint32_t app_id,
298 base::TimeTicks navigation_start_time) {
299 DCHECK(waiting_for_on_will_navigate_ack_);
300 DVLOG(2) << "Frame::OnWillNavigateAck id=" << id_;
301 waiting_for_on_will_navigate_ack_ = false;
302 ChangeClient(frame_client, std::move(user_data),
303 std::move(window_tree_client), app_id, navigation_start_time);
304 if (pending_navigate_.get())
305 StartNavigate(std::move(pending_navigate_));
306 }
307
308 void Frame::SetWindow(mus::Window* window) {
309 DCHECK(!window_);
310 DCHECK_EQ(id_, window->id());
311 window_ = window;
312 window_->SetLocalProperty(kFrame, this);
313 window_->AddObserver(this);
314 if (pending_navigate_.get())
315 StartNavigate(std::move(pending_navigate_));
316 }
317
318 void Frame::BuildFrameTree(std::vector<const Frame*>* frames) const {
319 frames->push_back(this);
320 for (const Frame* frame : children_)
321 frame->BuildFrameTree(frames);
322 }
323
324 void Frame::Add(Frame* node) {
325 DCHECK(!node->parent_);
326
327 node->parent_ = this;
328 children_.push_back(node);
329
330 tree_->root()->NotifyAdded(this, node, tree_->AdvanceChangeID());
331 }
332
333 void Frame::Remove(Frame* node) {
334 DCHECK_EQ(node->parent_, this);
335 auto iter = std::find(children_.begin(), children_.end(), node);
336 DCHECK(iter != children_.end());
337 node->parent_ = nullptr;
338 children_.erase(iter);
339
340 tree_->root()->NotifyRemoved(this, node, tree_->AdvanceChangeID());
341 }
342
343 void Frame::StartNavigate(mojo::URLRequestPtr request) {
344 if (waiting_for_on_will_navigate_ack_) {
345 // We're waiting for OnWillNavigate(). We need to wait until that completes
346 // before attempting any other loads.
347 pending_navigate_ = std::move(request);
348 return;
349 }
350
351 pending_navigate_.reset();
352
353 // We need a Window to navigate. When we get the Window we'll complete the
354 // navigation.
355 if (!window_) {
356 pending_navigate_ = std::move(request);
357 return;
358 }
359
360 // Drop any pending navigation requests.
361 navigate_weak_ptr_factory_.InvalidateWeakPtrs();
362
363 DVLOG(2) << "Frame::StartNavigate id=" << id_ << " url=" << request->url;
364
365 const GURL requested_url(request->url.get());
366 base::TimeTicks navigation_start_time =
367 base::TimeTicks::FromInternalValue(request->originating_time_ticks);
368 tree_->delegate_->CanNavigateFrame(
369 this, std::move(request),
370 base::Bind(&Frame::OnCanNavigateFrame,
371 navigate_weak_ptr_factory_.GetWeakPtr(), requested_url,
372 navigation_start_time));
373 }
374
375 void Frame::OnCanNavigateFrame(
376 const GURL& url,
377 base::TimeTicks navigation_start_time,
378 uint32_t app_id,
379 mojom::FrameClient* frame_client,
380 scoped_ptr<FrameUserData> user_data,
381 mus::mojom::WindowTreeClientPtr window_tree_client) {
382 TRACE_EVENT1("web_view", "Frame::OnCanNavigateFrame",
383 "url", url.possibly_invalid_spec());
384
385 DVLOG(2) << "Frame::OnCanNavigateFrame id=" << id_
386 << " equal=" << (AreAppIdsEqual(app_id, app_id_) ? "true" : "false");
387 if (AreAppIdsEqual(app_id, app_id_)) {
388 // The app currently rendering the frame will continue rendering it. In this
389 // case we do not use the WindowTreeClient (because the app has a Window
390 // already
391 // and ends up reusing it).
392 DCHECK(!window_tree_client);
393 ChangeClient(frame_client, std::move(user_data),
394 std::move(window_tree_client), app_id, navigation_start_time);
395 } else {
396 waiting_for_on_will_navigate_ack_ = true;
397 DCHECK(window_tree_client);
398 // TODO(sky): url isn't correct here, it should be a security origin.
399 frame_client_->OnWillNavigate(
400 url.spec(),
401 base::Bind(&Frame::OnWillNavigateAck, base::Unretained(this),
402 frame_client, base::Passed(&user_data),
403 base::Passed(&window_tree_client), app_id,
404 navigation_start_time));
405 }
406 }
407
408 void Frame::NotifyAdded(const Frame* source,
409 const Frame* added_node,
410 uint32_t change_id) {
411 // |frame_client_| may be null during initial frame creation and parenting.
412 if (frame_client_)
413 frame_client_->OnFrameAdded(change_id, FrameToFrameData(added_node));
414
415 for (Frame* child : children_)
416 child->NotifyAdded(source, added_node, change_id);
417 }
418
419 void Frame::NotifyRemoved(const Frame* source,
420 const Frame* removed_node,
421 uint32_t change_id) {
422 frame_client_->OnFrameRemoved(change_id, removed_node->id());
423
424 for (Frame* child : children_)
425 child->NotifyRemoved(source, removed_node, change_id);
426 }
427
428 void Frame::NotifyClientPropertyChanged(const Frame* source,
429 const mojo::String& name,
430 const mojo::Array<uint8_t>& value) {
431 if (this != source)
432 frame_client_->OnFrameClientPropertyChanged(source->id(), name,
433 value.Clone());
434
435 for (Frame* child : children_)
436 child->NotifyClientPropertyChanged(source, name, value);
437 }
438
439 void Frame::NotifyFrameLoadingStateChanged(const Frame* frame, bool loading) {
440 frame_client_->OnFrameLoadingStateChanged(frame->id(), loading);
441 }
442
443 void Frame::NotifyDispatchFrameLoadEvent(const Frame* frame) {
444 frame_client_->OnDispatchFrameLoadEvent(frame->id());
445 }
446
447 void Frame::OnTreeChanged(const TreeChangeParams& params) {
448 if (params.new_parent && this == tree_->root()) {
449 Frame* child_frame = FindFrame(params.target->id());
450 if (child_frame && !child_frame->window_)
451 child_frame->SetWindow(params.target);
452 }
453 }
454
455 void Frame::OnWindowDestroying(mus::Window* window) {
456 if (parent_)
457 parent_->Remove(this);
458
459 // Reset |window_ownership_| so we don't attempt to delete |window_| in the
460 // destructor.
461 window_ownership_ = WindowOwnership::DOESNT_OWN_WINDOW;
462
463 if (tree_->root() == this) {
464 window_->RemoveObserver(this);
465 window_ = nullptr;
466 return;
467 }
468
469 delete this;
470 }
471
472 void Frame::OnWindowEmbeddedAppDisconnected(mus::Window* window) {
473 // See FrameTreeDelegate::OnWindowEmbeddedAppDisconnected() for details of
474 // when
475 // this happens.
476 //
477 // Currently we have no way to distinguish between the cases that lead to this
478 // being called, so we assume we can continue on. Continuing on is important
479 // for html as it's entirely possible for a page to create a frame, navigate
480 // to a bogus url and expect the frame to still exist.
481 tree_->delegate_->OnWindowEmbeddedInFrameDisconnected(this);
482 }
483
484 void Frame::PostMessageEventToFrame(uint32_t target_frame_id,
485 mojom::HTMLMessageEventPtr event) {
486 // NOTE: |target_frame_id| is allowed to be from another connection.
487 Frame* target = tree_->root()->FindFrame(target_frame_id);
488 if (!target || target == this ||
489 !tree_->delegate_->CanPostMessageEventToFrame(this, target, event.get()))
490 return;
491
492 target->frame_client_->OnPostMessageEvent(id_, target_frame_id,
493 std::move(event));
494 }
495
496 void Frame::LoadingStateChanged(bool loading, double progress) {
497 bool loading_state_changed = loading_ != loading;
498 loading_ = loading;
499 progress_ = progress;
500 tree_->LoadingStateChanged();
501
502 if (loading_state_changed && parent_ &&
503 !AreAppIdsEqual(app_id_, parent_->app_id_)) {
504 // We need to notify the parent if it is in a different app, so that it can
505 // keep track of this frame's loading state. If the parent is in the same
506 // app, we assume that the loading state is propagated directly within the
507 // app itself and no notification is needed from our side.
508 parent_->NotifyFrameLoadingStateChanged(this, loading_);
509 }
510 }
511
512 void Frame::TitleChanged(const mojo::String& title) {
513 // Only care about title changes on the root frame.
514 if (!parent_)
515 tree_->TitleChanged(title);
516 }
517
518 void Frame::DidCommitProvisionalLoad() {
519 tree_->DidCommitProvisionalLoad(this);
520 }
521
522 void Frame::SetClientProperty(const mojo::String& name,
523 mojo::Array<uint8_t> value) {
524 auto iter = client_properties_.find(name);
525 const bool already_in_map = (iter != client_properties_.end());
526 if (value.is_null()) {
527 if (!already_in_map)
528 return;
529 client_properties_.erase(iter);
530 } else {
531 std::vector<uint8_t> as_vector(value.To<std::vector<uint8_t>>());
532 if (already_in_map && iter->second == as_vector)
533 return;
534 client_properties_[name] = as_vector;
535 }
536 tree_->ClientPropertyChanged(this, name, value);
537 }
538
539 void Frame::OnCreatedFrame(
540 mojo::InterfaceRequest<mojom::Frame> frame_request,
541 mojom::FrameClientPtr client,
542 uint32_t frame_id,
543 mojo::Map<mojo::String, mojo::Array<uint8_t>> client_properties) {
544 if ((frame_id >> 16) != embedded_connection_id_) {
545 // TODO(sky): kill connection here?
546 DVLOG(1) << "OnCreatedFrame supplied invalid frame id, expecting"
547 << embedded_connection_id_;
548 return;
549 }
550
551 if (FindFrame(frame_id)) {
552 // TODO(sky): kill connection here?
553 DVLOG(1) << "OnCreatedFrame supplied id of existing frame.";
554 return;
555 }
556
557 Frame* child_frame = tree_->CreateChildFrame(
558 this, std::move(frame_request), std::move(client), frame_id, app_id_,
559 client_properties.To<ClientPropertyMap>());
560 child_frame->embedded_connection_id_ = embedded_connection_id_;
561 }
562
563 void Frame::RequestNavigate(mojom::NavigationTargetType target_type,
564 uint32_t target_frame_id,
565 mojo::URLRequestPtr request) {
566 if (target_type == mojom::NavigationTargetType::EXISTING_FRAME) {
567 // |target_frame| is allowed to come from another connection.
568 Frame* target_frame = tree_->root()->FindFrame(target_frame_id);
569 if (!target_frame) {
570 DVLOG(1) << "RequestNavigate EXISTING_FRAME with no matching frame";
571 return;
572 }
573 if (target_frame != tree_->root()) {
574 target_frame->StartNavigate(std::move(request));
575 return;
576 }
577 // Else case if |target_frame| == root. Treat at top level request.
578 }
579 tree_->delegate_->NavigateTopLevel(this, std::move(request));
580 }
581
582 void Frame::DidNavigateLocally(const mojo::String& url) {
583 tree_->DidNavigateLocally(this, url.To<GURL>());
584 }
585
586 void Frame::DispatchLoadEventToParent() {
587 if (parent_ && !AreAppIdsEqual(app_id_, parent_->app_id_)) {
588 // Send notification to fire a load event in the parent, if the parent is in
589 // a different app. If the parent is in the same app, we assume that the app
590 // itself handles firing load event directly and no notification is needed
591 // from our side.
592 parent_->NotifyDispatchFrameLoadEvent(this);
593 }
594 }
595
596 void Frame::OnFindInFrameCountUpdated(int32_t request_id,
597 int32_t count,
598 bool final_update) {
599 tree_->delegate_->OnFindInFrameCountUpdated(request_id, this, count,
600 final_update);
601 }
602
603 void Frame::OnFindInPageSelectionUpdated(int32_t request_id,
604 int32_t active_match_ordinal) {
605 tree_->delegate_->OnFindInPageSelectionUpdated(request_id, this,
606 active_match_ordinal);
607 }
608
609 } // namespace web_view
OLDNEW
« no previous file with comments | « components/web_view/frame.h ('k') | components/web_view/frame_apptest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698