1// Copyright (c) 2012 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 "ppapi/proxy/flash_menu_resource.h"
6
7#include "ppapi/c/pp_errors.h"
8#include "ppapi/proxy/ppapi_messages.h"
9#include "ppapi/proxy/serialized_flash_menu.h"
10
11namespace ppapi {
12namespace proxy {
13
14FlashMenuResource::FlashMenuResource(Connection connection,
15                                     PP_Instance instance)
16    : PluginResource(connection, instance),
17      selected_id_dest_(NULL) {
18}
19
20FlashMenuResource::~FlashMenuResource() {
21}
22
23bool FlashMenuResource::Initialize(const PP_Flash_Menu* menu_data) {
24  SerializedFlashMenu serialized_menu;
25  if (!menu_data || !serialized_menu.SetPPMenu(menu_data))
26    return false;
27  SendCreate(RENDERER, PpapiHostMsg_FlashMenu_Create(serialized_menu));
28  return true;
29}
30
31thunk::PPB_Flash_Menu_API* FlashMenuResource::AsPPB_Flash_Menu_API() {
32  return this;
33}
34
35int32_t FlashMenuResource::Show(
36    const PP_Point* location,
37    int32_t* selected_id,
38    scoped_refptr<TrackedCallback> callback) {
39  if (TrackedCallback::IsPending(callback_))
40    return PP_ERROR_INPROGRESS;
41
42  selected_id_dest_ = selected_id;
43  callback_ = callback;
44
45  // This must be a sync message even though we don't care about the result.
46  // The actual reply will be sent asynchronously in the future. This sync
47  // request is due to the following deadlock:
48  //
49  //  1. Flash sends a show request to the renderer.
50  //  2. The show handler in the renderer (in the case of full screen) requests
51  //     the window rect which is a sync message to the browser. This causes
52  //     a nested message loop to be spun up in the renderer.
53  //  3. Flash expects context menus to be synchronous so it starts a nested
54  //     message loop. This creates a second nested message loop in both the
55  //     plugin and renderer process.
56  //  4. The browser sends the window rect reply to unblock the renderer, but
57  //     it's in the second nested message loop and the reply will *not*
58  //     unblock this loop.
59  //  5. The second loop won't exit until the message loop is complete, but
60  //     that can't start until the first one exits.
61  //
62  // Having this message sync forces the sync request from the renderer to the
63  // browser for the window rect will complete before Flash can run a nested
64  // message loop to wait for the result of the menu.
65  SyncCall<IPC::Message>(RENDERER, PpapiHostMsg_FlashMenu_Show(*location));
66  return PP_OK_COMPLETIONPENDING;
67}
68
69void FlashMenuResource::OnReplyReceived(
70    const proxy::ResourceMessageReplyParams& params,
71    const IPC::Message& msg) {
72  // Because the Show call is synchronous but we ignore the sync result, we
73  // can't use the normal reply dispatch and have to do it manually here.
74  switch (msg.type()) {
75    case PpapiPluginMsg_FlashMenu_ShowReply::ID: {
76      int32_t selected_id;
77      if (ppapi::UnpackMessage<PpapiPluginMsg_FlashMenu_ShowReply>(
78              msg, &selected_id))
79        OnShowReply(params, selected_id);
80      break;
81    }
82  }
83}
84
85void FlashMenuResource::OnShowReply(
86    const proxy::ResourceMessageReplyParams& params,
87    int32_t selected_id) {
88  if (!TrackedCallback::IsPending(callback_))
89    return;
90
91  *selected_id_dest_ = selected_id;
92  selected_id_dest_ = NULL;
93  callback_->Run(params.result());
94}
95
96}  // namespace proxy
97}  // namespace ppapi
98