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.
6 #include "web/DevToolsEmulator.h"
8 #include "core/frame/FrameHost.h"
9 #include "core/frame/FrameView.h"
10 #include "core/frame/Settings.h"
11 #include "core/page/Page.h"
12 #include "platform/RuntimeEnabledFeatures.h"
13 #include "public/platform/WebLayerTreeView.h"
14 #include "public/web/WebDeviceEmulationParams.h"
15 #include "web/InspectorEmulationAgent.h"
16 #include "web/WebInputEventConversion.h"
17 #include "web/WebLocalFrameImpl.h"
18 #include "web/WebSettingsImpl.h"
19 #include "web/WebViewImpl.h"
23 static float calculateDeviceScaleAdjustment(int width
, int height
, float deviceScaleFactor
)
25 // Chromium on Android uses a device scale adjustment for fonts used in text autosizing for
26 // improved legibility. This function computes this adjusted value for text autosizing.
27 // For a description of the Android device scale adjustment algorithm, see:
28 // chrome/browser/chrome_content_browser_client.cc, GetDeviceScaleAdjustment(...)
29 if (!width
|| !height
|| !deviceScaleFactor
)
32 static const float kMinFSM
= 1.05f
;
33 static const int kWidthForMinFSM
= 320;
34 static const float kMaxFSM
= 1.3f
;
35 static const int kWidthForMaxFSM
= 800;
37 float minWidth
= std::min(width
, height
) / deviceScaleFactor
;
38 if (minWidth
<= kWidthForMinFSM
)
40 if (minWidth
>= kWidthForMaxFSM
)
43 // The font scale multiplier varies linearly between kMinFSM and kMaxFSM.
44 float ratio
= static_cast<float>(minWidth
- kWidthForMinFSM
) / (kWidthForMaxFSM
- kWidthForMinFSM
);
45 return ratio
* (kMaxFSM
- kMinFSM
) + kMinFSM
;
52 DevToolsEmulator::DevToolsEmulator(WebViewImpl
* webViewImpl
)
53 : m_webViewImpl(webViewImpl
)
54 , m_emulationAgent(nullptr)
55 , m_deviceMetricsEnabled(false)
56 , m_emulateMobileEnabled(false)
57 , m_isOverlayScrollbarsEnabled(false)
58 , m_originalDefaultMinimumPageScaleFactor(0)
59 , m_originalDefaultMaximumPageScaleFactor(0)
60 , m_embedderTextAutosizingEnabled(webViewImpl
->page()->settings().textAutosizingEnabled())
61 , m_embedderDeviceScaleAdjustment(webViewImpl
->page()->settings().deviceScaleAdjustment())
62 , m_embedderPreferCompositingToLCDTextEnabled(webViewImpl
->page()->settings().preferCompositingToLCDTextEnabled())
63 , m_embedderUseMobileViewport(webViewImpl
->page()->settings().useMobileViewportStyle())
64 , m_embedderPluginsEnabled(webViewImpl
->page()->settings().pluginsEnabled())
65 , m_embedderAvailablePointerTypes(webViewImpl
->page()->settings().availablePointerTypes())
66 , m_embedderPrimaryPointerType(webViewImpl
->page()->settings().primaryPointerType())
67 , m_embedderAvailableHoverTypes(webViewImpl
->page()->settings().availableHoverTypes())
68 , m_embedderPrimaryHoverType(webViewImpl
->page()->settings().primaryHoverType())
69 , m_touchEventEmulationEnabled(false)
70 , m_doubleTapToZoomEnabled(false)
71 , m_originalTouchEnabled(false)
72 , m_originalDeviceSupportsMouse(false)
73 , m_originalDeviceSupportsTouch(false)
74 , m_originalMaxTouchPoints(0)
75 , m_embedderScriptEnabled(webViewImpl
->page()->settings().scriptEnabled())
76 , m_scriptExecutionDisabled(false)
77 , m_hidePinchScrollbarsNearMinScale(false)
81 DevToolsEmulator::~DevToolsEmulator()
85 PassOwnPtrWillBeRawPtr
<DevToolsEmulator
> DevToolsEmulator::create(WebViewImpl
* webViewImpl
)
87 return adoptPtrWillBeNoop(new DevToolsEmulator(webViewImpl
));
90 DEFINE_TRACE(DevToolsEmulator
)
92 visitor
->trace(m_emulationAgent
);
95 void DevToolsEmulator::setEmulationAgent(InspectorEmulationAgent
* agent
)
97 m_emulationAgent
= agent
;
100 void DevToolsEmulator::viewportChanged()
102 if (m_emulationAgent
)
103 m_emulationAgent
->viewportChanged();
106 void DevToolsEmulator::setTextAutosizingEnabled(bool enabled
)
108 m_embedderTextAutosizingEnabled
= enabled
;
109 bool emulateMobileEnabled
= m_deviceMetricsEnabled
&& m_emulateMobileEnabled
;
110 if (!emulateMobileEnabled
)
111 m_webViewImpl
->page()->settings().setTextAutosizingEnabled(enabled
);
114 void DevToolsEmulator::setDeviceScaleAdjustment(float deviceScaleAdjustment
)
116 m_embedderDeviceScaleAdjustment
= deviceScaleAdjustment
;
117 bool emulateMobileEnabled
= m_deviceMetricsEnabled
&& m_emulateMobileEnabled
;
118 if (!emulateMobileEnabled
)
119 m_webViewImpl
->page()->settings().setDeviceScaleAdjustment(deviceScaleAdjustment
);
122 void DevToolsEmulator::setPreferCompositingToLCDTextEnabled(bool enabled
)
124 m_embedderPreferCompositingToLCDTextEnabled
= enabled
;
125 bool emulateMobileEnabled
= m_deviceMetricsEnabled
&& m_emulateMobileEnabled
;
126 if (!emulateMobileEnabled
)
127 m_webViewImpl
->page()->settings().setPreferCompositingToLCDTextEnabled(enabled
);
130 void DevToolsEmulator::setUseMobileViewportStyle(bool enabled
)
132 m_embedderUseMobileViewport
= enabled
;
133 bool emulateMobileEnabled
= m_deviceMetricsEnabled
&& m_emulateMobileEnabled
;
134 if (!emulateMobileEnabled
)
135 m_webViewImpl
->page()->settings().setUseMobileViewportStyle(enabled
);
138 void DevToolsEmulator::setPluginsEnabled(bool enabled
)
140 m_embedderPluginsEnabled
= enabled
;
141 bool emulateMobileEnabled
= m_deviceMetricsEnabled
&& m_emulateMobileEnabled
;
142 if (!emulateMobileEnabled
)
143 m_webViewImpl
->page()->settings().setPluginsEnabled(enabled
);
146 void DevToolsEmulator::setScriptEnabled(bool enabled
)
148 m_embedderScriptEnabled
= enabled
;
149 if (!m_scriptExecutionDisabled
)
150 m_webViewImpl
->page()->settings().setScriptEnabled(enabled
);
153 void DevToolsEmulator::setDoubleTapToZoomEnabled(bool enabled
)
155 m_doubleTapToZoomEnabled
= enabled
;
158 bool DevToolsEmulator::doubleTapToZoomEnabled() const
160 return m_touchEventEmulationEnabled
? true : m_doubleTapToZoomEnabled
;
163 void DevToolsEmulator::setHidePinchScrollbarsNearMinScale(bool enabled
)
165 m_hidePinchScrollbarsNearMinScale
= enabled
;
166 if (m_webViewImpl
->layerTreeView())
167 m_webViewImpl
->layerTreeView()->setHidePinchScrollbarsNearMinScale(enabled
);
170 void DevToolsEmulator::setAvailablePointerTypes(int types
)
172 m_embedderAvailablePointerTypes
= types
;
173 bool emulateMobileEnabled
= m_deviceMetricsEnabled
&& m_emulateMobileEnabled
;
174 if (!emulateMobileEnabled
)
175 m_webViewImpl
->page()->settings().setAvailablePointerTypes(types
);
178 void DevToolsEmulator::setPrimaryPointerType(PointerType pointerType
)
180 m_embedderPrimaryPointerType
= pointerType
;
181 bool emulateMobileEnabled
= m_deviceMetricsEnabled
&& m_emulateMobileEnabled
;
182 if (!emulateMobileEnabled
)
183 m_webViewImpl
->page()->settings().setPrimaryPointerType(pointerType
);
186 void DevToolsEmulator::setAvailableHoverTypes(int types
)
188 m_embedderAvailableHoverTypes
= types
;
189 bool emulateMobileEnabled
= m_deviceMetricsEnabled
&& m_emulateMobileEnabled
;
190 if (!emulateMobileEnabled
)
191 m_webViewImpl
->page()->settings().setAvailableHoverTypes(types
);
194 void DevToolsEmulator::setPrimaryHoverType(HoverType hoverType
)
196 m_embedderPrimaryHoverType
= hoverType
;
197 bool emulateMobileEnabled
= m_deviceMetricsEnabled
&& m_emulateMobileEnabled
;
198 if (!emulateMobileEnabled
)
199 m_webViewImpl
->page()->settings().setPrimaryHoverType(hoverType
);
202 void DevToolsEmulator::enableDeviceEmulation(const WebDeviceEmulationParams
& params
)
204 if (!m_deviceMetricsEnabled
) {
205 m_deviceMetricsEnabled
= true;
206 if (params
.viewSize
.width
|| params
.viewSize
.height
)
207 m_webViewImpl
->setBackgroundColorOverride(Color::darkGray
);
208 m_webViewImpl
->updateShowFPSCounter();
211 m_webViewImpl
->page()->settings().setDeviceScaleAdjustment(calculateDeviceScaleAdjustment(params
.viewSize
.width
, params
.viewSize
.height
, params
.deviceScaleFactor
));
213 if (params
.screenPosition
== WebDeviceEmulationParams::Mobile
)
214 enableMobileEmulation();
216 disableMobileEmulation();
218 m_webViewImpl
->setCompositorDeviceScaleFactorOverride(params
.deviceScaleFactor
);
219 m_webViewImpl
->setRootLayerTransform(WebSize(params
.offset
.x
, params
.offset
.y
), params
.scale
);
220 if (Document
* document
= m_webViewImpl
->mainFrameImpl()->frame()->document())
221 document
->mediaQueryAffectingValueChanged();
224 void DevToolsEmulator::disableDeviceEmulation()
226 if (!m_deviceMetricsEnabled
)
229 m_deviceMetricsEnabled
= false;
230 m_webViewImpl
->setBackgroundColorOverride(Color::transparent
);
231 m_webViewImpl
->updateShowFPSCounter();
232 m_webViewImpl
->page()->settings().setDeviceScaleAdjustment(m_embedderDeviceScaleAdjustment
);
233 disableMobileEmulation();
234 m_webViewImpl
->setCompositorDeviceScaleFactorOverride(0.f
);
235 m_webViewImpl
->setRootLayerTransform(WebSize(0.f
, 0.f
), 1.f
);
236 m_webViewImpl
->setPageScaleFactor(1.f
);
237 if (Document
* document
= m_webViewImpl
->mainFrameImpl()->frame()->document())
238 document
->mediaQueryAffectingValueChanged();
241 void DevToolsEmulator::enableMobileEmulation()
243 if (m_emulateMobileEnabled
)
245 m_emulateMobileEnabled
= true;
246 m_isOverlayScrollbarsEnabled
= RuntimeEnabledFeatures::overlayScrollbarsEnabled();
247 RuntimeEnabledFeatures::setOverlayScrollbarsEnabled(true);
248 m_webViewImpl
->enableViewport();
249 m_webViewImpl
->settings()->setViewportMetaEnabled(true);
250 m_webViewImpl
->page()->frameHost().visualViewport().initializeScrollbars();
251 m_webViewImpl
->settings()->setShrinksViewportContentToFit(true);
252 m_webViewImpl
->page()->settings().setTextAutosizingEnabled(true);
253 m_webViewImpl
->page()->settings().setPreferCompositingToLCDTextEnabled(true);
254 m_webViewImpl
->page()->settings().setUseMobileViewportStyle(true);
255 m_webViewImpl
->page()->settings().setPluginsEnabled(false);
256 m_webViewImpl
->page()->settings().setAvailablePointerTypes(PointerTypeCoarse
);
257 m_webViewImpl
->page()->settings().setPrimaryPointerType(PointerTypeCoarse
);
258 m_webViewImpl
->page()->settings().setAvailableHoverTypes(HoverTypeOnDemand
);
259 m_webViewImpl
->page()->settings().setPrimaryHoverType(HoverTypeOnDemand
);
260 m_webViewImpl
->setZoomFactorOverride(1);
262 m_originalDefaultMinimumPageScaleFactor
= m_webViewImpl
->defaultMinimumPageScaleFactor();
263 m_originalDefaultMaximumPageScaleFactor
= m_webViewImpl
->defaultMaximumPageScaleFactor();
264 m_webViewImpl
->setDefaultPageScaleLimits(0.25f
, 5);
267 void DevToolsEmulator::disableMobileEmulation()
269 if (!m_emulateMobileEnabled
)
271 RuntimeEnabledFeatures::setOverlayScrollbarsEnabled(m_isOverlayScrollbarsEnabled
);
272 m_webViewImpl
->disableViewport();
273 m_webViewImpl
->settings()->setViewportMetaEnabled(false);
274 m_webViewImpl
->page()->frameHost().visualViewport().initializeScrollbars();
275 m_webViewImpl
->settings()->setShrinksViewportContentToFit(false);
276 m_webViewImpl
->page()->settings().setTextAutosizingEnabled(m_embedderTextAutosizingEnabled
);
277 m_webViewImpl
->page()->settings().setPreferCompositingToLCDTextEnabled(m_embedderPreferCompositingToLCDTextEnabled
);
278 m_webViewImpl
->page()->settings().setUseMobileViewportStyle(m_embedderUseMobileViewport
);
279 m_webViewImpl
->page()->settings().setPluginsEnabled(m_embedderPluginsEnabled
);
280 m_webViewImpl
->page()->settings().setAvailablePointerTypes(m_embedderAvailablePointerTypes
);
281 m_webViewImpl
->page()->settings().setPrimaryPointerType(m_embedderPrimaryPointerType
);
282 m_webViewImpl
->page()->settings().setAvailableHoverTypes(m_embedderAvailableHoverTypes
);
283 m_webViewImpl
->page()->settings().setPrimaryHoverType(m_embedderPrimaryHoverType
);
284 m_webViewImpl
->setZoomFactorOverride(0);
285 m_emulateMobileEnabled
= false;
286 m_webViewImpl
->setDefaultPageScaleLimits(
287 m_originalDefaultMinimumPageScaleFactor
,
288 m_originalDefaultMaximumPageScaleFactor
);
291 void DevToolsEmulator::setTouchEventEmulationEnabled(bool enabled
)
293 if (m_touchEventEmulationEnabled
== enabled
)
295 if (!m_touchEventEmulationEnabled
) {
296 m_originalTouchEnabled
= RuntimeEnabledFeatures::touchEnabled();
297 m_originalDeviceSupportsMouse
= m_webViewImpl
->page()->settings().deviceSupportsMouse();
298 m_originalDeviceSupportsTouch
= m_webViewImpl
->page()->settings().deviceSupportsTouch();
299 m_originalMaxTouchPoints
= m_webViewImpl
->page()->settings().maxTouchPoints();
301 RuntimeEnabledFeatures::setTouchEnabled(enabled
? true : m_originalTouchEnabled
);
302 if (!m_originalDeviceSupportsTouch
) {
303 m_webViewImpl
->page()->settings().setDeviceSupportsMouse(enabled
? false : m_originalDeviceSupportsMouse
);
304 m_webViewImpl
->page()->settings().setDeviceSupportsTouch(enabled
? true : m_originalDeviceSupportsTouch
);
305 // Currently emulation does not provide multiple touch points.
306 m_webViewImpl
->page()->settings().setMaxTouchPoints(enabled
? 1 : m_originalMaxTouchPoints
);
308 m_touchEventEmulationEnabled
= enabled
;
309 m_webViewImpl
->mainFrameImpl()->frameView()->layout();
312 void DevToolsEmulator::setScriptExecutionDisabled(bool scriptExecutionDisabled
)
314 m_scriptExecutionDisabled
= scriptExecutionDisabled
;
315 m_webViewImpl
->page()->settings().setScriptEnabled(m_scriptExecutionDisabled
? false : m_embedderScriptEnabled
);
318 bool DevToolsEmulator::handleInputEvent(const WebInputEvent
& inputEvent
)
320 Page
* page
= m_webViewImpl
->page();
324 // FIXME: This workaround is required for touch emulation on Mac, where
325 // compositor-side pinch handling is not enabled. See http://crbug.com/138003.
326 bool isPinch
= inputEvent
.type
== WebInputEvent::GesturePinchBegin
|| inputEvent
.type
== WebInputEvent::GesturePinchUpdate
|| inputEvent
.type
== WebInputEvent::GesturePinchEnd
;
327 if (isPinch
&& m_touchEventEmulationEnabled
) {
328 FrameView
* frameView
= page
->deprecatedLocalMainFrame()->view();
329 PlatformGestureEventBuilder
gestureEvent(frameView
, static_cast<const WebGestureEvent
&>(inputEvent
));
330 float pageScaleFactor
= page
->pageScaleFactor();
331 if (gestureEvent
.type() == PlatformEvent::GesturePinchBegin
) {
332 m_lastPinchAnchorCss
= adoptPtr(new IntPoint(frameView
->scrollPosition() + gestureEvent
.position()));
333 m_lastPinchAnchorDip
= adoptPtr(new IntPoint(gestureEvent
.position()));
334 m_lastPinchAnchorDip
->scale(pageScaleFactor
, pageScaleFactor
);
336 if (gestureEvent
.type() == PlatformEvent::GesturePinchUpdate
&& m_lastPinchAnchorCss
) {
337 float newPageScaleFactor
= pageScaleFactor
* gestureEvent
.scale();
338 IntPoint
anchorCss(*m_lastPinchAnchorDip
.get());
339 anchorCss
.scale(1.f
/ newPageScaleFactor
, 1.f
/ newPageScaleFactor
);
340 m_webViewImpl
->setPageScaleFactor(newPageScaleFactor
);
341 m_webViewImpl
->mainFrame()->setScrollOffset(toIntSize(*m_lastPinchAnchorCss
.get() - toIntSize(anchorCss
)));
343 if (gestureEvent
.type() == PlatformEvent::GesturePinchEnd
) {
344 m_lastPinchAnchorCss
.clear();
345 m_lastPinchAnchorDip
.clear();