Index: content/public/android/java/src/org/chromium/content/browser/DialogSurfaceActivityMapper.java |
diff --git a/content/public/android/java/src/org/chromium/content/browser/DialogSurfaceActivityMapper.java b/content/public/android/java/src/org/chromium/content/browser/DialogSurfaceActivityMapper.java |
index 915b20c04e486a3128cbeacd2ea8bd60b69bd3ca..61e76cf623d1538403742a5056d4798965e2c3f4 100644 |
--- a/content/public/android/java/src/org/chromium/content/browser/DialogSurfaceActivityMapper.java |
+++ b/content/public/android/java/src/org/chromium/content/browser/DialogSurfaceActivityMapper.java |
@@ -10,10 +10,12 @@ import android.os.RemoteException; |
import org.chromium.base.Log; |
import org.chromium.base.annotations.CalledByNative; |
-import org.chromium.base.Log; |
import org.chromium.media.IDialogSurfaceActivityMapper; |
import org.chromium.media.IDialogSurfaceHolder; |
+import java.util.HashMap; |
+import java.util.HashSet; |
+ |
/** |
* Implementation of IDialogSurfaceActivityMapper. |
* Note that this doesn't have to be exposed via binder if the surfaces are |
@@ -21,16 +23,53 @@ import org.chromium.media.IDialogSurfaceHolder; |
* process, since that's where the information about the activity is. |
* Provided by ChildProcessLauncher to DialogSurfaceManager. |
*/ |
-public class DialogSurfaceActivityMapper extends IDialogSurfaceActivityMapper.Stub { |
+public class DialogSurfaceActivityMapper |
+ extends IDialogSurfaceActivityMapper.Stub implements ContentViewCore.Observer { |
private static final String TAG = "cr_DialogSurfaceAM"; |
+ private class ClientSet extends HashSet<IDialogSurfaceHolder> {} |
+ private class ClientMap extends HashMap<ContentViewCore, ClientSet> {} |
+ private class ReverseClientMap extends HashMap<IDialogSurfaceHolder, ContentViewCore> {} |
+ // Access mClients with mClientLock held. |
+ private final Object mClientLock = new Object(); |
+ private final ClientMap mClients = new ClientMap(); |
+ private final ReverseClientMap mReverseClients = new ReverseClientMap(); |
+ |
+ // This may be called on any thread. |
@Override |
- public void postWindowToken(int rendererPid, int renderFrameId, IDialogSurfaceHolder holder) { |
+ public void registerHolder(int rendererPid, int renderFrameId, IDialogSurfaceHolder holder) { |
nativeCallBackWithContentViewCore(rendererPid, renderFrameId, this, holder); |
} |
+ // This may be called on any thread. |
+ @Override |
+ public void unregisterHolder(IDialogSurfaceHolder holder) { |
+ // If we switched threads here, then we could remove mClientLock and |
+ // also unregister for callbacks with CVC. |
+ synchronized (mClientLock) { |
+ // Remove holder from both the forward and reverse tables. |
+ ContentViewCore cvc = mReverseClients.get(holder); |
+ if (cvc == null) return; |
+ |
+ mReverseClients.remove(holder); |
+ |
+ ClientSet clients = mClients.get(cvc); |
+ if (clients != null) { |
+ clients.remove(holder); |
+ if (clients.size() == 0) { |
+ // We could cvc.removeObserver(this) here, but we're on the |
+ // wrong thread. Instead, simply ignore it and skip the |
+ // callbacks if needed. We could probably remove it on the |
+ // next callback, which will be on the correct thread. |
+ mClients.remove(cvc); |
+ } |
+ } |
+ } |
+ } |
+ |
/** |
* Receive a callback from native with a previously requested ContentViewCore. |
+ * This is called on the UI thread. |
*/ |
@CalledByNative |
private void onContentViewCore(IDialogSurfaceHolder holder, ContentViewCore cvc) { |
@@ -41,15 +80,94 @@ public class DialogSurfaceActivityMapper extends IDialogSurfaceActivityMapper.St |
if (activity != null) { |
token = activity.getWindow().getDecorView().getRootView().getWindowToken(); |
} |
+ |
+ // Register to get updates about window changes, so that we know |
+ // when the token changes. |
+ cvc.addObserver(this); |
} |
try { |
holder.onWindowToken(token); |
+ addClient(cvc, holder); |
+ // TODO(liberato): clean up if holder exits. |
} catch (RemoteException e) { |
Log.e(TAG, "Unable to post token " + token + " to DialogSurfaceHolder", e); |
} |
} |
+ // This will be called on the UI thread. |
+ @Override |
+ public void onContentViewCoreDestroyed(ContentViewCore cvc) { |
+ ClientSet clients = mClients.get(cvc); |
+ if (clients == null) return; |
+ |
+ // Why not just send back the CVC, and have the holder manager setting |
+ // up callbacks? It's only a little weird, because (a) media/ can't |
+ // access ContentViewCore, and (b) we're guaranteed to run in the |
+ // browser, while DialogSurface might not. |
+ for (IDialogSurfaceHolder holder : clients) { |
+ try { |
+ holder.onWindowToken(null); |
+ } catch (RemoteException e) { |
+ } |
+ } |
+ |
+ mClients.remove(cvc); |
+ } |
+ |
+ // This will be called on the UI thread. |
+ @Override |
+ public void onAttachedToWindowAndroid(ContentViewCore cvc) { |
+ Activity activity = cvc.getWindowAndroid().getActivity().get(); |
+ IBinder token = null; |
+ if (activity != null) { |
+ token = activity.getWindow().getDecorView().getRootView().getWindowToken(); |
+ } |
+ |
+ // Don't bother to call back if there's no token anyway. |
+ if (token == null) return; |
+ |
+ synchronized (mClientLock) { |
+ ClientSet clients = mClients.get(cvc); |
+ if (clients == null) return; |
+ |
+ for (IDialogSurfaceHolder holder : clients) { |
+ try { |
+ holder.onWindowToken(token); |
+ } catch (RemoteException e) { |
+ } |
+ } |
+ } |
+ } |
+ |
+ // This will be called on the UI thread. |
+ @Override |
+ public void onDetachedFromWindowAndroid(ContentViewCore cvc) { |
+ synchronized (mClientLock) { |
+ ClientSet clients = mClients.get(cvc); |
+ if (clients == null) return; |
+ |
+ for (IDialogSurfaceHolder holder : clients) { |
+ try { |
+ holder.onWindowToken(null); |
+ } catch (RemoteException e) { |
+ } |
+ } |
+ } |
+ } |
+ |
+ private void addClient(ContentViewCore cvc, IDialogSurfaceHolder holder) { |
+ synchronized (mClientLock) { |
+ ClientSet clients = mClients.get(cvc); |
+ if (clients == null) { |
+ clients = new ClientSet(); |
+ mClients.put(cvc, clients); |
+ } |
+ clients.add(holder); |
+ mReverseClients.put(holder, cvc); |
+ } |
+ } |
+ |
// Look up the ContentViewCore for (rendererPid, renderFrameId), and call |
// back onContentViewCore with it and |holder|. The callback will happen |
// on the browser UI thread. |