|
|
Chromium Code Reviews|
Created:
4 years, 2 months ago by lanwei Modified:
3 years, 10 months ago CC:
chromium-reviews, yusukes+watch_chromium.org, shuchen+watch_chromium.org, jam, dtapuska+chromiumwatch_chromium.org, nona+watch_chromium.org, darin-cc_chromium.org, James Su Target Ref:
refs/pending/heads/master Project:
chromium Visibility:
Public. |
DescriptionSend pointerleave and pointerenter events for styluses on Mac.
We should send the correct pointerleave and pointerenter events when the stylus leaves or enters
the proximity of the tablet on Mac. We will check isEnteringProximity on NSTabletProximity event
to decide the position of styluses. Now we only consider about a simple case that there is only
one pointer device.
The details of how we send pointer leave and enter events are in the doc:
https://docs.google.com/document/d/1O_unOvamElhB_FANTMFJjjG9OIgCxZNGTVSfJzecaSk/
BUG=624810
Review-Url: https://codereview.chromium.org/2373733002
Cr-Commit-Position: refs/heads/master@{#448729}
Committed: https://chromium.googlesource.com/chromium/src/+/e97eb52977dd7ed3936227f0d240cfeed865e520
Patch Set 1 #Patch Set 2 : Ready for review #
Total comments: 4
Patch Set 3 : pointer leave #Patch Set 4 : rebase #
Messages
Total messages: 48 (30 generated)
Description was changed from ========== pointer leave pointer leave mac pointe leave mac pointe pressure BUG= ========== to ========== Send pointerleave and pointerenter events for styluses on Mac. We should send the correct pointerleave and pointerenter events when the stylus leaves or enters the proximity of the tablet on Mac. We will check isEnteringProximity on NSTabletProximity event to decide the position of styluses. BUG=624810 ==========
lanwei@chromium.org changed reviewers: + ccameron@chromium.org, mustaq@chromium.org, tdresser@chromium.org
The CQ bit was checked by lanwei@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
lanwei@chromium.org changed reviewers: + nzolghadr@chromium.org
Description was changed from ========== Send pointerleave and pointerenter events for styluses on Mac. We should send the correct pointerleave and pointerenter events when the stylus leaves or enters the proximity of the tablet on Mac. We will check isEnteringProximity on NSTabletProximity event to decide the position of styluses. BUG=624810 ========== to ========== Send pointerleave and pointerenter events for styluses on Mac. We should send the correct pointerleave and pointerenter events when the stylus leaves or enters the proximity of the tablet on Mac. We will check isEnteringProximity on NSTabletProximity event to decide the position of styluses. Now we only consider about the simple case that there is one pointer device. BUG=624810 ==========
Description was changed from ========== Send pointerleave and pointerenter events for styluses on Mac. We should send the correct pointerleave and pointerenter events when the stylus leaves or enters the proximity of the tablet on Mac. We will check isEnteringProximity on NSTabletProximity event to decide the position of styluses. Now we only consider about the simple case that there is one pointer device. BUG=624810 ========== to ========== Send pointerleave and pointerenter events for styluses on Mac. We should send the correct pointerleave and pointerenter events when the stylus leaves or enters the proximity of the tablet on Mac. We will check isEnteringProximity on NSTabletProximity event to decide the position of styluses. Now we only consider about a simple case that there is only one pointer device. BUG=624810 ==========
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Exceeded global retry quota
The CQ bit was checked by lanwei@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
Patchset #2 (id:20001) has been deleted
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: This issue passed the CQ dry run.
I think that the easier way to approach this is to use the -[NSEvent pointingDeviceType:] API. It appears that you can query this for mouseDown and mouseUp and mouseDragged events (not sure which other ones do it).
On 2016/09/29 23:35:38, ccameron wrote: > I think that the easier way to approach this is to use the -[NSEvent > pointingDeviceType:] API. It appears that you can query this for mouseDown and > mouseUp and mouseDragged events (not sure which other ones do it). (so, in that case, you could just query that in -mouseEvent)
On 2016/09/29 23:36:00, ccameron wrote: > On 2016/09/29 23:35:38, ccameron wrote: > > I think that the easier way to approach this is to use the -[NSEvent > > pointingDeviceType:] API. It appears that you can query this for mouseDown and > > mouseUp and mouseDragged events (not sure which other ones do it). > > (so, in that case, you could just query that in -mouseEvent) I tried to query pointingDeviceType on mouse events, but NSMouseExited and NSMouseEntered events throw an error. I looked the developer guide for Cocoa events, Pointing devices information is belong to proximity events, https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Ev... We really need to decide the pointer types of NSMouseExited and NSMouseEntered events in order to send pointer enter and leaves.
On 2016/09/30 14:50:04, lanwei wrote: > On 2016/09/29 23:36:00, ccameron wrote: > > On 2016/09/29 23:35:38, ccameron wrote: > > > I think that the easier way to approach this is to use the -[NSEvent > > > pointingDeviceType:] API. It appears that you can query this for mouseDown > and > > > mouseUp and mouseDragged events (not sure which other ones do it). > > > > (so, in that case, you could just query that in -mouseEvent) > > I tried to query pointingDeviceType on mouse events, but NSMouseExited and > NSMouseEntered events throw an error. I looked the developer guide for Cocoa > events, Pointing devices information is belong to proximity events, > https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Ev... > > We really need to decide the pointer types of NSMouseExited and NSMouseEntered > events in order to send pointer enter and leaves. Good point. NSMouseEntered and NSMouseExited can be a bit difficult, because they are fired when the window gets focus -- so, it could be that pressing "command-tab" on the keyboard (bringing the window to the front) results in the "pointerenter" event. So, it's not clear to me which pointer device should "get" these sorts of events. In the case where just moving the pointer causes the mouse to enter, it might be easier ... but it also may not be. Do you know if we get the NSMouseEntered or NSTabletProximity first? If we get the NSMouseEntered event first (even when a tablet is present), it could be hard to define the correct behavior. We should make sure we decide the answers to these questions (and we should make sure we're doing the same thing as Mozilla & MS). Btw, do you have a tablet device to test with -- we should probably get one in MTV/SFO to work on Mac bugs, so we could use some recommendations.
On 2016/10/03 21:30:04, ccameron wrote: > On 2016/09/30 14:50:04, lanwei wrote: > > On 2016/09/29 23:36:00, ccameron wrote: > > > On 2016/09/29 23:35:38, ccameron wrote: > > > > I think that the easier way to approach this is to use the -[NSEvent > > > > pointingDeviceType:] API. It appears that you can query this for mouseDown > > and > > > > mouseUp and mouseDragged events (not sure which other ones do it). > > > > > > (so, in that case, you could just query that in -mouseEvent) > > > > I tried to query pointingDeviceType on mouse events, but NSMouseExited and > > NSMouseEntered events throw an error. I looked the developer guide for Cocoa > > events, Pointing devices information is belong to proximity events, > > > https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Ev... > > > > We really need to decide the pointer types of NSMouseExited and NSMouseEntered > > events in order to send pointer enter and leaves. > > Good point. > > NSMouseEntered and NSMouseExited can be a bit difficult, because they are fired > when the window gets focus -- so, it could be that pressing "command-tab" on the > keyboard (bringing the window to the front) results in the "pointerenter" event. > So, it's not clear to me which pointer device should "get" these sorts of > events. Sorry, I should make this clear to you that this patch is for stylus only and we assume that for mouse and touch, we are sending pointerleave and pointerenter events correctly right now. For the "command-tab" switching windows, we think that pointerleave and pointerenter events should be sent for all pointer types. But for stylus, there is an extra pointerleave and pointerenter events, which are sent when the stylus is approaching or leaving the tablet device. > In the case where just moving the pointer causes the mouse to enter, it might be > easier ... but it also may not be. Do you know if we get the NSMouseEntered or > NSTabletProximity first? If we get the NSMouseEntered event first (even when a > tablet is present), it could be hard to define the correct behavior. We will get NSTabletProximity whenever a stylus is approaching or leaving the tablet device with isEnteringProximity either true or false. But we will get NSMouseEntered only when the cursor moves into the window. When the stylus leaves the tablet, the cursor will stay wherever it was, so NSMouseEntered is not sent if the cursor is inside the window. > > We should make sure we decide the answers to these questions (and we should make > sure we're doing the same thing as Mozilla & MS). I tested on both firefox and Edge, our behavior is basically matching with them. I will write a doc to summarize them. > > Btw, do you have a tablet device to test with -- we should probably get one in > MTV/SFO to work on Mac bugs, so we could use some recommendations. Yes, we have a wacome intuos pro, it is better to have one for testing!
This looks very close but a slightly foolproof pointerType handling would be great. There are many corner cases to handle from JS perspective. E.g. if mouse was on window originally, then pen entered proximity within the window, the JS needs to know that the pen is moving not the mouse. As we discussed offline today, we will need to send extra event to Blink to make this sane for JS. Here is the solution we think covers all cases: - Have two boolean state vars: pageSeesMouse, pageSeesPen, with exactly one true at any given time. - If any of the state switches to true, send a |leave| event for the other type, flip the other state to false, send an |enter| or |move| for this type. - State switch is decided by current event's type. If type is missing, infer from the current |isStylusInProximity| state. - Only render_widget_host_view_mac should determine the type; the event builder will blindly follow the pre-determined type. https://codereview.chromium.org/2373733002/diff/40001/content/browser/rendere... File content/browser/renderer_host/input/web_input_event_builders_mac.mm (right): https://codereview.chromium.org/2373733002/diff/40001/content/browser/rendere... content/browser/renderer_host/input/web_input_event_builders_mac.mm:401: // We decide their pointer types by checking if we recevied a Please clarify that the check is done at the caller: "...by checking (at the caller) if..." https://codereview.chromium.org/2373733002/diff/40001/content/browser/rendere... content/browser/renderer_host/input/web_input_event_builders_mac.mm:418: // NSTabletPointEventSubtype. This comment looks a bit stale: should say "a subtype of NSTabletPointEventSubtype or NSTabletProximityEventSubtype", right? https://codereview.chromium.org/2373733002/diff/40001/content/browser/rendere... content/browser/renderer_host/input/web_input_event_builders_mac.mm:419: result.pointerType = blink::WebPointerProperties::PointerType::Pen; The pen must be in proximity in this case right? https://codereview.chromium.org/2373733002/diff/40001/content/browser/rendere... File content/browser/renderer_host/render_widget_host_view_mac.mm (right): https://codereview.chromium.org/2373733002/diff/40001/content/browser/rendere... content/browser/renderer_host/render_widget_host_view_mac.mm:1711: isStylusEnteringProximity_ = false; I think the name |isStylusInProximity| better reflects the state.
On 2016/10/14 16:07:17, mustaq wrote: > This looks very close but a slightly foolproof pointerType handling would be > great. There are many corner cases to handle from JS perspective. E.g. if mouse > was on window originally, then pen entered proximity within the window, the JS > needs to know that the pen is moving not the mouse. > > As we discussed offline today, we will need to send extra event to Blink to make > this sane for JS. Here is the solution we think covers all cases: > - Have two boolean state vars: pageSeesMouse, pageSeesPen, with exactly one true > at any given time. > - If any of the state switches to true, send a |leave| event for the other type, > flip the other state to false, send an |enter| or |move| for this type. > - State switch is decided by current event's type. If type is missing, infer > from the current |isStylusInProximity| state. > - Only render_widget_host_view_mac should determine the type; the event builder > will blindly follow the pre-determined type. > > https://codereview.chromium.org/2373733002/diff/40001/content/browser/rendere... > File content/browser/renderer_host/input/web_input_event_builders_mac.mm > (right): > > https://codereview.chromium.org/2373733002/diff/40001/content/browser/rendere... > content/browser/renderer_host/input/web_input_event_builders_mac.mm:401: // We > decide their pointer types by checking if we recevied a > Please clarify that the check is done at the caller: "...by checking (at the > caller) if..." > > https://codereview.chromium.org/2373733002/diff/40001/content/browser/rendere... > content/browser/renderer_host/input/web_input_event_builders_mac.mm:418: // > NSTabletPointEventSubtype. > This comment looks a bit stale: should say "a subtype of > NSTabletPointEventSubtype or NSTabletProximityEventSubtype", right? > > https://codereview.chromium.org/2373733002/diff/40001/content/browser/rendere... > content/browser/renderer_host/input/web_input_event_builders_mac.mm:419: > result.pointerType = blink::WebPointerProperties::PointerType::Pen; > The pen must be in proximity in this case right? > > https://codereview.chromium.org/2373733002/diff/40001/content/browser/rendere... > File content/browser/renderer_host/render_widget_host_view_mac.mm (right): > > https://codereview.chromium.org/2373733002/diff/40001/content/browser/rendere... > content/browser/renderer_host/render_widget_host_view_mac.mm:1711: > isStylusEnteringProximity_ = false; > I think the name |isStylusInProximity| better reflects the state. Let's outline a proposed solution in a doc before experimenting.
On 2016/10/14 16:08:58, mustaq wrote: > On 2016/10/14 16:07:17, mustaq wrote: > > This looks very close but a slightly foolproof pointerType handling would be > > great. There are many corner cases to handle from JS perspective. E.g. if > mouse > > was on window originally, then pen entered proximity within the window, the JS > > needs to know that the pen is moving not the mouse. > > > > As we discussed offline today, we will need to send extra event to Blink to > make > > this sane for JS. Here is the solution we think covers all cases: > > - Have two boolean state vars: pageSeesMouse, pageSeesPen, with exactly one > true > > at any given time. > > - If any of the state switches to true, send a |leave| event for the other > type, > > flip the other state to false, send an |enter| or |move| for this type. > > - State switch is decided by current event's type. If type is missing, infer > > from the current |isStylusInProximity| state. > > - Only render_widget_host_view_mac should determine the type; the event > builder > > will blindly follow the pre-determined type. > > > > > https://codereview.chromium.org/2373733002/diff/40001/content/browser/rendere... > > File content/browser/renderer_host/input/web_input_event_builders_mac.mm > > (right): > > > > > https://codereview.chromium.org/2373733002/diff/40001/content/browser/rendere... > > content/browser/renderer_host/input/web_input_event_builders_mac.mm:401: // We > > decide their pointer types by checking if we recevied a > > Please clarify that the check is done at the caller: "...by checking (at the > > caller) if..." > > > > > https://codereview.chromium.org/2373733002/diff/40001/content/browser/rendere... > > content/browser/renderer_host/input/web_input_event_builders_mac.mm:418: // > > NSTabletPointEventSubtype. > > This comment looks a bit stale: should say "a subtype of > > NSTabletPointEventSubtype or NSTabletProximityEventSubtype", right? > > > > > https://codereview.chromium.org/2373733002/diff/40001/content/browser/rendere... > > content/browser/renderer_host/input/web_input_event_builders_mac.mm:419: > > result.pointerType = blink::WebPointerProperties::PointerType::Pen; > > The pen must be in proximity in this case right? > > > > > https://codereview.chromium.org/2373733002/diff/40001/content/browser/rendere... > > File content/browser/renderer_host/render_widget_host_view_mac.mm (right): > > > > > https://codereview.chromium.org/2373733002/diff/40001/content/browser/rendere... > > content/browser/renderer_host/render_widget_host_view_mac.mm:1711: > > isStylusEnteringProximity_ = false; > > I think the name |isStylusInProximity| better reflects the state. > > Let's outline a proposed solution in a doc before experimenting. Here is Lan's doc outlining various cases with pen enter/leave: https://docs.google.com/a/google.com/document/d/1O_unOvamElhB_FANTMFJjjG9OIgC...
On 2017/01/25 19:57:33, mustaq wrote: > On 2016/10/14 16:08:58, mustaq wrote: > > On 2016/10/14 16:07:17, mustaq wrote: > > > This looks very close but a slightly foolproof pointerType handling would be > > > great. There are many corner cases to handle from JS perspective. E.g. if > > mouse > > > was on window originally, then pen entered proximity within the window, the > JS > > > needs to know that the pen is moving not the mouse. > > > > > > As we discussed offline today, we will need to send extra event to Blink to > > make > > > this sane for JS. Here is the solution we think covers all cases: > > > - Have two boolean state vars: pageSeesMouse, pageSeesPen, with exactly one > > true > > > at any given time. > > > - If any of the state switches to true, send a |leave| event for the other > > type, > > > flip the other state to false, send an |enter| or |move| for this type. > > > - State switch is decided by current event's type. If type is missing, infer > > > from the current |isStylusInProximity| state. > > > - Only render_widget_host_view_mac should determine the type; the event > > builder > > > will blindly follow the pre-determined type. > > > > > > > > > https://codereview.chromium.org/2373733002/diff/40001/content/browser/rendere... > > > File content/browser/renderer_host/input/web_input_event_builders_mac.mm > > > (right): > > > > > > > > > https://codereview.chromium.org/2373733002/diff/40001/content/browser/rendere... > > > content/browser/renderer_host/input/web_input_event_builders_mac.mm:401: // > We > > > decide their pointer types by checking if we recevied a > > > Please clarify that the check is done at the caller: "...by checking (at the > > > caller) if..." > > > > > > > > > https://codereview.chromium.org/2373733002/diff/40001/content/browser/rendere... > > > content/browser/renderer_host/input/web_input_event_builders_mac.mm:418: // > > > NSTabletPointEventSubtype. > > > This comment looks a bit stale: should say "a subtype of > > > NSTabletPointEventSubtype or NSTabletProximityEventSubtype", right? > > > > > > > > > https://codereview.chromium.org/2373733002/diff/40001/content/browser/rendere... > > > content/browser/renderer_host/input/web_input_event_builders_mac.mm:419: > > > result.pointerType = blink::WebPointerProperties::PointerType::Pen; > > > The pen must be in proximity in this case right? > > > > > > > > > https://codereview.chromium.org/2373733002/diff/40001/content/browser/rendere... > > > File content/browser/renderer_host/render_widget_host_view_mac.mm (right): > > > > > > > > > https://codereview.chromium.org/2373733002/diff/40001/content/browser/rendere... > > > content/browser/renderer_host/render_widget_host_view_mac.mm:1711: > > > isStylusEnteringProximity_ = false; > > > I think the name |isStylusInProximity| better reflects the state. > > > > Let's outline a proposed solution in a doc before experimenting. > > Here is Lan's doc outlining various cases with pen enter/leave: > https://docs.google.com/a/google.com/document/d/1O_unOvamElhB_FANTMFJjjG9OIgC... LGTM, the event sequence in the doc (generated through this patch) looks reasonable.
Description was changed from ========== Send pointerleave and pointerenter events for styluses on Mac. We should send the correct pointerleave and pointerenter events when the stylus leaves or enters the proximity of the tablet on Mac. We will check isEnteringProximity on NSTabletProximity event to decide the position of styluses. Now we only consider about a simple case that there is only one pointer device. BUG=624810 ========== to ========== Send pointerleave and pointerenter events for styluses on Mac. We should send the correct pointerleave and pointerenter events when the stylus leaves or enters the proximity of the tablet on Mac. We will check isEnteringProximity on NSTabletProximity event to decide the position of styluses. Now we only consider about a simple case that there is only one pointer device. The details of how we send pointer leave and enter events are in the doc: https://docs.google.com/document/d/1O_unOvamElhB_FANTMFJjjG9OIgCxZNGTVSfJzecaSk/ BUG=624810 ==========
This looks good I was searching through to see where the device ID in the doc would come from, and I noticed that this doesn't support multiple tablet devices There's a short write-up about handling multiple tablets here: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Ev... We'll probably want to manage a map from deviceID+pointingDeviceID to "javascript device ID" in the browser.
On 2017/01/25 21:10:13, ccameron wrote: > This looks good > > I was searching through to see where the device ID in the doc would come from, > and I noticed that this doesn't support multiple tablet devices > > There's a short write-up about handling multiple tablets here: > > https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Ev... > > We'll probably want to manage a map from deviceID+pointingDeviceID to > "javascript device ID" in the browser. Thank you for catching up the multiple tablets cases. We fired a bug to support multi-pointer on multi-device. https://bugs.chromium.org/p/chromium/issues/detail?id=685803 Our current testing device Wacom only supports one active pen at a time, and now we are only support one tablet. There are some issues with multi-stylus right now, like the cursor position, now we only have one position. We will support multi-stylus from multi-tablet soon in the future. I tested right now with one tablet, different pen, the deviceID will give different values.
lgtm, I'd prefer to have multi-device support from the start, but this is fine
The CQ bit was checked by lanwei@chromium.org
CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Try jobs failed on following builders: ios-device-xcode-clang on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/ios-device-xcode-...) ios-simulator on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/ios-simulator/bui...) ios-simulator-xcode-clang on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/ios-simulator-xco...)
The CQ bit was checked by lanwei@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: mac_chromium_compile_dbg_ng on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/mac_chromium_comp...)
The CQ bit was checked by lanwei@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
Patchset #4 (id:80001) has been deleted
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: This issue passed the CQ dry run.
The CQ bit was checked by lanwei@chromium.org
The patchset sent to the CQ was uploaded after l-g-t-m from ccameron@chromium.org, mustaq@chromium.org Link to the patchset: https://codereview.chromium.org/2373733002/#ps100001 (title: "rebase")
CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
CQ is committing da patch.
Bot data: {"patchset_id": 100001, "attempt_start_ts": 1486500530434320,
"parent_rev": "fc72782d44b71f6a1d8b38f264c9119dcb6cea65", "commit_rev":
"e97eb52977dd7ed3936227f0d240cfeed865e520"}
Message was sent while issue was closed.
Description was changed from ========== Send pointerleave and pointerenter events for styluses on Mac. We should send the correct pointerleave and pointerenter events when the stylus leaves or enters the proximity of the tablet on Mac. We will check isEnteringProximity on NSTabletProximity event to decide the position of styluses. Now we only consider about a simple case that there is only one pointer device. The details of how we send pointer leave and enter events are in the doc: https://docs.google.com/document/d/1O_unOvamElhB_FANTMFJjjG9OIgCxZNGTVSfJzecaSk/ BUG=624810 ========== to ========== Send pointerleave and pointerenter events for styluses on Mac. We should send the correct pointerleave and pointerenter events when the stylus leaves or enters the proximity of the tablet on Mac. We will check isEnteringProximity on NSTabletProximity event to decide the position of styluses. Now we only consider about a simple case that there is only one pointer device. The details of how we send pointer leave and enter events are in the doc: https://docs.google.com/document/d/1O_unOvamElhB_FANTMFJjjG9OIgCxZNGTVSfJzecaSk/ BUG=624810 Review-Url: https://codereview.chromium.org/2373733002 Cr-Commit-Position: refs/heads/master@{#448729} Committed: https://chromium.googlesource.com/chromium/src/+/e97eb52977dd7ed3936227f0d240... ==========
Message was sent while issue was closed.
Committed patchset #4 (id:100001) as https://chromium.googlesource.com/chromium/src/+/e97eb52977dd7ed3936227f0d240... |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
