1 | // Copyright 2013 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 | package org.chromium.content.browser.test; |
6 | |
7 | import android.os.Handler; |
8 | import android.os.Looper; |
9 | import android.os.Message; |
10 | import android.os.MessageQueue; |
11 | import android.util.Log; |
12 | |
13 | import org.chromium.base.CalledByNative; |
14 | import org.chromium.base.JNINamespace; |
15 | import java.lang.reflect.Method; |
16 | import java.lang.reflect.Field; |
17 | import java.lang.reflect.InvocationTargetException; |
18 | |
19 | /** |
20 | * Handles processing messages in nested run loops. |
21 | * |
22 | * Android does not support nested message loops by default. While running |
23 | * in nested mode, we use reflection to retreive messages from the MessageQueue |
24 | * and dispatch them. |
25 | */ |
26 | @JNINamespace("content") |
27 | class NestedSystemMessageHandler { |
28 | // See org.chromium.base.SystemMessageHandler for more message ids. |
29 | // The id here should not conflict with the ones in SystemMessageHandler. |
30 | private static final int QUIT_MESSAGE = 10; |
31 | private static final Handler mHandler = new Handler(); |
32 | |
33 | private NestedSystemMessageHandler() { |
34 | } |
35 | |
36 | /** |
37 | * Processes messages from the current MessageQueue till the queue becomes idle. |
38 | */ |
39 | @SuppressWarnings("unused") |
40 | @CalledByNative |
41 | private boolean runNestedLoopTillIdle() { |
42 | boolean quitLoop = false; |
43 | |
44 | MessageQueue queue = Looper.myQueue(); |
45 | queue.addIdleHandler(new MessageQueue.IdleHandler() { |
46 | @Override |
47 | public boolean queueIdle() { |
48 | mHandler.sendMessage(mHandler.obtainMessage(QUIT_MESSAGE)); |
49 | return false; |
50 | } |
51 | }); |
52 | |
53 | Class<?> messageQueueClazz = queue.getClass(); |
54 | Method nextMethod = null; |
55 | try { |
56 | nextMethod = messageQueueClazz.getDeclaredMethod("next"); |
57 | } catch (SecurityException e) { |
58 | e.printStackTrace(); |
59 | return false; |
60 | } catch (NoSuchMethodException e) { |
61 | e.printStackTrace(); |
62 | return false; |
63 | } |
64 | nextMethod.setAccessible(true); |
65 | |
66 | while (!quitLoop) { |
67 | Message msg = null; |
68 | try { |
69 | msg = (Message)nextMethod.invoke(queue); |
70 | } catch (IllegalArgumentException e) { |
71 | e.printStackTrace(); |
72 | return false; |
73 | } catch (IllegalAccessException e) { |
74 | e.printStackTrace(); |
75 | return false; |
76 | } catch (InvocationTargetException e) { |
77 | e.printStackTrace(); |
78 | return false; |
79 | } |
80 | |
81 | if (msg != null) { |
82 | if (msg.what == QUIT_MESSAGE) { |
83 | quitLoop = true; |
84 | } |
85 | Class messageClazz = msg.getClass(); |
86 | Field targetFiled = null; |
87 | try { |
88 | targetFiled = messageClazz.getDeclaredField("target"); |
89 | } catch (SecurityException e) { |
90 | e.printStackTrace(); |
91 | return false; |
92 | } catch (NoSuchFieldException e) { |
93 | e.printStackTrace(); |
94 | return false; |
95 | } |
96 | targetFiled.setAccessible(true); |
97 | |
98 | Handler target = null; |
99 | try { |
100 | target = (Handler) targetFiled.get(msg); |
101 | } catch (IllegalArgumentException e) { |
102 | e.printStackTrace(); |
103 | return false; |
104 | } catch (IllegalAccessException e) { |
105 | e.printStackTrace(); |
106 | return false; |
107 | } |
108 | |
109 | if (target == null) { |
110 | // No target is a magic identifier for the quit message. |
111 | quitLoop = true; |
112 | } |
113 | |
114 | target.dispatchMessage(msg); |
115 | msg.recycle(); |
116 | } else { |
117 | quitLoop = true; |
118 | } |
119 | } |
120 | return true; |
121 | } |
122 | |
123 | @SuppressWarnings("unused") |
124 | @CalledByNative |
125 | private static NestedSystemMessageHandler create() { |
126 | return new NestedSystemMessageHandler(); |
127 | } |
128 | } |