Commit Graph

49 Commits

Author SHA1 Message Date
David Aurelio
78d6988461 Add YogaNodeProperties implementation with ByteBuffer based setters
Summary:
@public
Adds an implementation of `YogaNodeProperties` that sets style properties using a `ByteBuffer` rather than JNI calls.
We hope for a speed improvement.

Reviewed By: pasqualeanatriello

Differential Revision: D9042225

fbshipit-source-id: c7f2b24eaeddd1190755bec85a5034079bd2f492
2018-07-30 09:43:37 -07:00
David Aurelio
3499e2e0ef Add YogaNodeProperties implementation based on ByteBuffer
Summary:
@public
Adds an implementation of `YogaNodeProperties` that accesses style and layout properties using a `ByteBuffer` rather than JNI calls.
We hope for a speed improvement.

This needs further cleanup after experimenting, e.g. to codegen the offsets.

Reviewed By: pasqualeanatriello

Differential Revision: D8911723

fbshipit-source-id: 3c24b57eb545155878896ebb5d64d4553eb6bedc
2018-07-30 09:43:37 -07:00
David Aurelio
b1821ab4cd move property storage into sub-object
Summary:
Here we introduce an abstraction over node property storage, in order to experiment with different approaches for Java/C integration.

- interface `YogaNodeProperties` as abstraction
- current JNI code factored into `YogaNodePropertiesJNI.java`
- `YogaNode` delegates all calls, no API changes

Reviewed By: astreet

Differential Revision: D8769448

fbshipit-source-id: e67327ce41fa047a51a986c652b3d59992a510e2
2018-07-30 09:43:37 -07:00
David Aurelio
966f5ece4a guards instead of nested if
Summary:
Replaces two nested if-blocks with guards. This is intended to help with restructuring this function in follow-ups.

@public

Reviewed By: priteshrnandgaonkar

Differential Revision: D8785659

fbshipit-source-id: 7b9d63e9814b83b999397c016ad67ad348bb0f72
2018-07-11 04:13:01 -07:00
David Aurelio
ede2888326 Lint / reformat YGJNI.cpp
Summary:
Run clangformat on `YGJNI.cpp`

@public

Reviewed By: priteshrnandgaonkar

Differential Revision: D8785660

fbshipit-source-id: 9748a5297e7b55e897de0280a79c2ea6ae1c1298
2018-07-11 04:13:00 -07:00
David Vacca
f4d29e6f11 Avoid cleaning up Owner of YGNode during clonning
Summary:
This diff refactors the cloning mechanism for YogaNode used from Fabric UI renderer and RN iOS graphs.
Previously, we were cleaning the owner of the child's cloned node inside the C++ implementation of YogaNode. This was a mistake because this modified the last commited YogaTree, causing side effect in RN iOS graphs.

Reviewed By: shergin

Differential Revision: D8672627

fbshipit-source-id: c9902d00690e0361fd58aed84b506c42258bd995
2018-06-28 22:47:50 -07:00
Pascal Hartig
46c96ee2cb Upgrade fbjni
Summary:
Sync fbjni with `libaries/fbjni`.

This includes a hack to fix a deadlock we found for cmake-debug variants only. This still gets us back in sync with other fbjni consumers which is going to save us trouble in the future if Yoga ever wants to make use of a newer feature.

Manual changes made in addition to the hand-crafted CMakeLists and cxx buck rules:

- `jni::isObjectRefType` has SDK lookup disabled.
- `fbjni/` path is changed back to `fb/` to retain compatibility with Yoga.

Reviewed By: priteshrnandgaonkar

Differential Revision: D8531991

fbshipit-source-id: 776f519e2e5f9bea37f55990348f7ed81c4860b4
2018-06-26 04:58:58 -07:00
David Vacca
5e3ffb39a2 Extend Yoga to be able clone Yoga Node with new children
Summary:
This diff includes the following changes:

1 ) I extended the Java implementation of YogaNode to be able to get a full copy of a YogaNode object without copying the List of children of the original YogaNode. In other words, the new copy of the YogaNode will have the same data of the original YogaNode, but with an empty list of children.

2 ) We created a new method in Yoga.cpp called YGNodeInsertSharedChild. This new method is going to be used by Fabric in order to temporarily share a YogaNode between two "Yoga Trees" (the "current Yoga" tree and a partial "clone of the current Yoga tree"). We exposed this new functionality in the java implementation of Yoga (method addSharedChildAt)

I'm including sebmarkbage for more context.

Reviewed By: emilsjolander

Differential Revision: D7245421

fbshipit-source-id: 72578c8261f29e4a12fc6c72a91f2f891cd58d48
2018-04-01 18:35:13 -07:00
David Vacca
f0edefdbb7 Rename YogaNode.parent -> YogaNode.owner
Summary:
In the persistent version of Yoga, a YogaNode can be shared between two YogaTrees, that means that a YogaNode could have more than one Parent at one point in time. That's why the concept of Parent of a YogaNode is not a 1-1 relationship anymore.
This diff changes the semantic of Parent of a YogaNode to Owner of a Yoga Node. CC sebmarkbage and priteshrnandgaonkar for more context.

Technically this diff renames the field YogaNode.parent to YogaNode.owner (and every internal field, Getter and Setter that is related to parent)

Note that as part of this diff I also modified the CSSLayoutDEPRECATED version of Yoga in order to keep compatibility with the C++ implementation.

Reviewed By: priteshrnandgaonkar

Differential Revision: D7352778

fbshipit-source-id: dcf1af5e72bfc3063b5c4bda197d7952a9194768
2018-04-01 18:35:13 -07:00
David Vacca
17901ea5c2 Refactor cloning of YogaNode
Summary: see Test Plan

Reviewed By: priteshrnandgaonkar

Differential Revision: D7339832

fbshipit-source-id: 2de6f47ae7601ac083d3b9fbe10ffaf6307ae760
2018-04-01 18:35:13 -07:00
Pritesh Nandgaonkar
3dfb68887d Expose layout diffing flag to java
Summary:
This diff exposes `shouldDiffLayoutWithoutLegacyStretchBehaviour` from YGConfig and `doesLegacyStretchFlagAffectsLayout` from YGLayout to YogaConfig and YogaNode respectively

Also added a positive test case. Didn't find the negative test case example. @[508947467:ianc] or @[759512522:emilsj], can you suggest a negative test case example.

Reviewed By: emilsjolander

Differential Revision: D7272067

fbshipit-source-id: e67e82eb057e4c7124904c715f9dca4dcfea21ea
2018-03-14 08:43:01 -07:00
Pritesh Nandgaonkar
3a82d2b1a8 Change NaN with large number
Summary:
Changed NaN with large number to support `-ffast-math` compiler flag.For  `-ffast-math` to work, all floating point numbers should be finite. Reason for not going with `FLT_MAX`, is that, it may cause number overflow during math operations. So thats why I opted for big number smaller than `FLT_MAX`.  Earlier we used NaN, while NaN is involved in comparision the comparision operator behaves differently, it always returns false. Also operators like, fmaxf,fminf etc. have wierd beahviours. This diff takes care of those things as far as possible, and all tests are passing.

Running ./instrumentation_tests/run instrumentation_tests/com/facebook/feed/ctacoalescing:ctacoalescing --class AttachmentCallToActionSelectorBenchmarkTest --benchmark --extra-arg iterations=100 shows the perf gain of 13-15%

Reviewed By: emilsjolander

Differential Revision: D6969537

fbshipit-source-id: bdc09eaf703e0d313ca65c25a4fb44c99203d9bf
2018-03-01 04:03:19 -08:00
Sophie Alpert
a2b6ddb7b1 Update license headers for MIT license
Summary:
Includes React Native and its dependencies Fresco, Metro, and Yoga. Excludes samples/examples/docs.

find: ^(?:( *)|( *(?:[\*~#]|::))( )? *)?Copyright (?:\(c\) )?(\d{4})\b.+Facebook[\s\S]+?BSD[\s\S]+?(?:this source tree|the same directory)\.$
replace: $1$2$3Copyright (c) $4-present, Facebook, Inc.\n$2\n$1$2$3This source code is licensed under the MIT license found in the\n$1$2$3LICENSE file in the root directory of this source tree.

Reviewed By: TheSavior, yungsters

Differential Revision: D7007050

fbshipit-source-id: 37dd6bf0ffec0923bfc99c260bb330683f35553e
2018-02-16 18:27:33 -08:00
David Vacca
747c2a4208 Make Java YogaNode cloneable
Summary:
This diff exposes the YogaNode clone operation to JNI in order to be able to clone Java YogaNode objects.

The clone method performs a shallow copy of the java YogaNode.

I made YogaNode to implement Cloneable, I know that this might not be a good idea but in this case it simplifies the cloning mechanism. I am open to suggestions.

IMPORTANT NOTES:
- The current implementation IS NOT making a deep copy of the mData instance variable.
- The mParent Java instance variable will reference the parent of the original Java YogaNode, is that ok sebmarkbage?

Reviewed By: priteshrnandgaonkar

Differential Revision: D6935971

fbshipit-source-id: a2008f1eb849b5074585b48699b7de56d5ac90d4
2018-02-14 18:12:34 -08:00
David Vacca
b1222bf83e Expose methods of persistent yoga for Java
Summary: This diff extends the JNI version of yoga in order to allow Java instances of the YogaConfig class to receive a callback when a Yoga node is cloned.

Reviewed By: priteshrnandgaonkar

Differential Revision: D6918605

fbshipit-source-id: e424c78680c04e21154ebe21405671c4e90f6529
2018-02-14 09:41:52 -08:00
Pritesh Nandgaonkar
6e38a26f32 Expose a function which marks all descendants dirty
Summary: Expose a function which marks all descendants dirty

Reviewed By: emilsjolander

Differential Revision: D6911869

fbshipit-source-id: e0a3abcf5653f921297edfdca473d83b947cc627
2018-02-08 05:23:46 -08:00
Pritesh Nandgaonkar
f5f8105b57 Made logging logic dynamic to log string length
Summary: Previously the logging logic assumed fixed number of characters in the string to be logged. With this diff the logging logic is made dynamic, catering to variable length of the string to be logged

Reviewed By: emilsjolander

Differential Revision: D6784491

fbshipit-source-id: 26e4520a84be355ff992b808297ce7a95b3d09e3
2018-01-23 06:41:41 -08:00
Pritesh Nandgaonkar
fbd332dee8 Make YGNode as c++ struct with properties exposed through accessors
Summary: Moved c implementation of `YGNode` to C++ struct. Not moving to C++ class as the React Classes dependent on `Yoga.h` assume it to be C. Thats why keeping `Yoga.h` C compatible. Sorry for the long diff, didn't thought that it will turn out to be this much big.Will keep an eye on number of lines next time 😉

Reviewed By: emilsjolander

Differential Revision: D6592257

fbshipit-source-id: 641e8b9462ad00731a094511f9f5608b23a6bb21
2017-12-19 11:33:07 -08:00
Pritesh Nandgaonkar
58d14ee557 Reverting the dirty child optimization
Summary: Reverting D6134754

Reviewed By: emilsjolander

Differential Revision: D6203290

fbshipit-source-id: 8e42abb70e55f0fac90faaa21ecdbe0fbb76ce6b
2017-10-31 23:23:01 -07:00
Pritesh Nandgaonkar
c5182c4bf5 Used hasDirtyChildren tag for the optimization
Summary:
More fine grained dirty marking

Currently a node's dirty flag propagates to the root of the tree ensuring that when any node is invalidated its whole subtree will be re-calculated. This is often times not needed. There are many properties which only effects a node's children and would not need to propagate all the way to the root such as align-items. Also in cases where the style does change layout it may not need to propagate all the way to the root but can often stop at the nearest position: absolute parent.

This change has the potential of greatly improving performance of re-calculating a tree.

This might require adding a second dirty flag named hasDirtyDescendants ensuring that traversal still works even though a parent is not marked as dirty.

Reviewed By: emilsjolander

Differential Revision: D6134754

fbshipit-source-id: bbcfee14058140b946401de756a3f130de0f51cd
2017-10-31 08:22:29 -07:00
Amir Shalem
16052085d0 Fix YogaLogger leakage when removing it from YogaConfig
Summary: -

Reviewed By: emilsjolander

Differential Revision: D5802035

fbshipit-source-id: 79c1f8c3cc5ddf3abd51206f23e076f2410cfc0c
2017-09-11 03:14:11 -07:00
Emil Sjolander
85c2e406e4 Fix flex basis not accounting for max size constraint
Summary: Fix flex basis not being constraint to the max size in the main direction. Previously this caused the added test to fail due to NaN in child dimensions.

Reviewed By: gkassabli

Differential Revision: D5044314

fbshipit-source-id: d9f9db832e4943a57a89c9d162ff6077b709795a
2017-05-12 09:12:51 -07:00
Lukas Wöhrl
91230ae177 Move YGLogger into YGConfig and associate YGNodeRef with log events
Summary:
Moves the `YGLogger` into `YGConfig` and pass the `YGNodeRef` into the logger to be able to associate the log messages and assertions with the specific node.

Tackles facebook/yoga#530 and facebook/yoga#446
Closes https://github.com/facebook/yoga/pull/531

Reviewed By: astreet

Differential Revision: D4970149

Pulled By: emilsjolander

fbshipit-source-id: b7fcdaa273143ea2fa35861620b2e4d79f04f0af
2017-05-03 09:30:25 -07:00
Lukas Wöhrl
203577724e Fix sizing of non strech items
Summary:
Fixes the sizing of items so that under most scenarios it calcultes its height by it's content for non exact measurings. This introduces a new useLegacyStretchBehaviour flag on the config to opt out of this change as it is breaking.

See facebook/yoga#505
Closes https://github.com/facebook/yoga/pull/506

Reviewed By: astreet

Differential Revision: D4954016

Pulled By: emilsjolander

fbshipit-source-id: d28bd5d174cd76951fb94df85e3b0cfab7f81ff7
2017-04-28 06:27:14 -07:00
Emil Sjolander
83fddd8af6 Expose print function to java
Summary: Allow printing a node from java

Reviewed By: astreet

Differential Revision: D4962456

fbshipit-source-id: 8f62ed6724490e621fbc11573b2a9b25c56e51f1
2017-04-27 16:28:34 -07:00
Emil Sjolander
8f9d7e243e Expose setPointScaleFactor to java
Summary: Expose setPointScaleFactor to java

Reviewed By: gkassabli

Differential Revision: D4953835

fbshipit-source-id: b1f97d9ec1bb78ccf7f53131fce87955fe66eb02
2017-04-26 12:35:00 -07:00
Lukas Wöhrl
5112564f08 Don't transfer layout outputs to java for unset edges
Summary:
See facebook/yoga#483. We should not transfer the layout if the layout didn't change. (using ```hasNewLayout```). This also changes that the lock on the java node is only aquired if needed, and it holds the lock only for the time the values are set and not for the time all it's children are set.
Closes https://github.com/facebook/yoga/pull/484

Reviewed By: astreet

Differential Revision: D4802966

Pulled By: emilsjolander

fbshipit-source-id: e8a8f2280ad6b25b98fc68b07eac68e0ec80fe3e
2017-04-01 04:43:09 -07:00
Lukas Wöhrl
5884ab7b76 Don't transfer layout outputs to java for nodes which don't have a new layout
Summary:
As suggested in facebook/yoga#484. This is the PR which only adds the part of using a local bool field for storing the hasLayoutFlag, to be able to pass the layout only if it has really changed.
Closes https://github.com/facebook/yoga/pull/492

Reviewed By: astreet

Differential Revision: D4786961

Pulled By: emilsjolander

fbshipit-source-id: cf3d354b93f6dcc3ef817ef73a47bd29e37d1848
2017-03-29 16:44:13 -07:00
Emil Sjolander
bc2fb5c7ab Expose UseWebDefaults to java
Summary: Expose UseWebDefaults to java

Reviewed By: astreet

Differential Revision: D4779743

fbshipit-source-id: 65a4184af6fb959fefff5c2014522c551ca440d5
2017-03-29 03:12:33 -07:00
Emil Sjolander
b283572453 Revert D4716024: [yoga] Avoid transfering cached layout information to java
Summary: This reverts commit c30763a6fc7426d653c7a6ca129615cddb4140e9

Differential Revision: D4716024

fbshipit-source-id: 7276b4bbf072aa444c5ae9fd1a3d62ea87a0cec1
2017-03-17 13:27:18 -07:00
Emil Sjolander
a14aeb27bb Avoid transfering cached layout information to java
Summary: Don't transfer layout outputs to java if the layout was cached as this means that it has already been transfered

Reviewed By: astreet

Differential Revision: D4716024

fbshipit-source-id: c30763a6fc7426d653c7a6ca129615cddb4140e9
2017-03-17 09:14:08 -07:00
Lukas Wöhrl
37c48257ae Move configuration to new YGConfig and pass them down to CalculateLayout
Summary:
Move configuration to new ```YGConfig``` and pass them down to CalculateLayout. See #418 .

Adds ```YGConfigNew()``` + ```YGConfigFree```, and changed ```YGSetExperimentalFeatureEnabled``` to use the config.

New function for calculation is ```YGNodeCalculateLayoutWithConfig```.
Closes https://github.com/facebook/yoga/pull/432

Reviewed By: astreet

Differential Revision: D4611359

Pulled By: emilsjolander

fbshipit-source-id: a1332f0e1b21cec02129dd021ee57408449e10b0
2017-03-01 09:27:53 -08:00
Lukas Wöhrl
1146013e9e Feature auto margin
Summary:
Even so I know there are some opinions against ```margin: 0 auto``` it's still part of the spec: https://www.w3.org/TR/css-flexbox-1/#auto-margins and pretty usefull if you have to position via ```justify-content```.

This PR adds an implementation for that.

It adds an additonal ```YGUnitAuto``` and margins got ```YGNodeStyleSetMarginAuto``` functions as well.
Closes https://github.com/facebook/yoga/pull/357

Reviewed By: astreet

Differential Revision: D4501142

Pulled By: emilsjolander

fbshipit-source-id: 86519f8632496f46e78a7c9dbc5b21e212e3e0c7
2017-02-14 14:27:48 -08:00
Emil Sjolander
6ceb4af2d4 Expose layout border to java
Summary: Expose layout border to java

Reviewed By: astreet

Differential Revision: D4543284

fbshipit-source-id: 6030915fc6f758e785a688f94c8ffbec7fbac8b8
2017-02-11 06:27:04 -08:00
Antonio Corrado
46817a38c3 Setting min=max dimension is treated as setting dimension
Summary: If a node has minDimension and maxDimension set at the same value, yoga will treat it as having a set dimension, doing less calculations.

Reviewed By: emilsjolander

Differential Revision: D4492395

fbshipit-source-id: 3f4293548399e006aa808b9586d24e77c7df1f21
2017-02-06 14:41:37 -08:00
Lukas Wöhrl
e567502750 Added property display: flex and none
Summary:
Fix #241 and successor for #302

Added new property ```display``` with ```YGDisplayFlex``` and ```YGDisplayNone```. Allows to hide nodes from the layout without the need to remove it from the DOM.
Closes https://github.com/facebook/yoga/pull/369

Reviewed By: astreet

Differential Revision: D4501141

Pulled By: emilsjolander

fbshipit-source-id: 0dfeee381f6d1e4bbba81926126b83dd7abab9d6
2017-02-06 09:39:37 -08:00
Emil Sjolander
56d06e27cf Pass parent with down with calculateLayout to allow percentages on root node
Summary: For percentage paddings/margins/sizes to work on the root node we need to have the ability to pass down the parent sizes. This has always been possible with the C API but was never exposed to java. This diff exposes this functionality.

Reviewed By: astreet

Differential Revision: D4501016

fbshipit-source-id: 0c9502e86ff200c021c78afb7ac4b48cf11b3bdb
2017-02-03 11:24:57 -08:00
Emil Sjolander
8775cdc13f Format and run codegen
Summary: run format & gentest scripts. Some of the javascript tests had not been generated recently by the looks of it.

Reviewed By: dshahidehpour

Differential Revision: D4459455

fbshipit-source-id: fc1eca58fe897c8f4a2571638b4f7035d023b479
2017-01-26 13:39:42 -08:00
Emil Sjolander
d391323129 Use findClassStatic instead of findClassLocal
Summary: We are saving the class reference in a static variable to should be using findClassStatic to ensure the reference is always valid.

Reviewed By: mhorowitz

Differential Revision: D4420352

fbshipit-source-id: 8c66c1b2213fe295334a9bdc4e1dd7e1a4285aae
2017-01-24 15:54:31 -08:00
Maël Nison
d70f289e73 Add YGLayoutGetMargin
Summary:
Fix #326. I'll open another PR once this one gets accepted to add support for `YGLayoutGetBorder` 👌
Closes https://github.com/facebook/yoga/pull/335

Reviewed By: gkassabli

Differential Revision: D4409399

Pulled By: emilsjolander

fbshipit-source-id: 8153f6701cab60b55a485f6d2e0b9f7767481090
2017-01-15 15:24:30 -08:00
Emil Sjolander
c04604dbc0 Implement java bindings for custom baseline function
Summary: Implement java bindings for custom baseline function

Differential Revision: D4392516

fbshipit-source-id: 39cf6066f8e5982268becd87e54c9ab51fbf7a90
2017-01-10 07:09:33 -08:00
Emil Sjolander
e539ecc9aa Expose layout paddding in java api
Summary: Expose layout padding api from D4376572

Reviewed By: passy

Differential Revision: D4377069

fbshipit-source-id: 2e0c04104560e1be586562b27b3457576590dc18
2017-01-05 12:54:45 -08:00
Lukas Woehrl
a85bd4ad2a Add feature to use percentage as value unit
Summary:
Adds the feature to use percentage as a value unit.

You can use the function ```YGPx(float)``` and ```YGPercent(float)``` for convenience.

I did some benchmarks:

```
Without Percentage Feature - Release x86:

Stack with flex: median: 0.000000 ms, stddev: 0.146683 ms
Align stretch in undefined axis: median: 0.000000 ms, stddev: 0.136525 ms
Nested flex: median: 0.000000 ms, stddev: 0.490101 ms
Huge nested layout: median: 23.000000 ms, stddev: 0.928291 ms

Stack with flex: median: 0.000000 ms, stddev: 0.170587 ms
Align stretch in undefined axis: median: 0.000000 ms, stddev: 0.143384 ms
Nested flex: median: 0.000000 ms, stddev: 0.477791 ms
Huge nested layout: median: 22.000000 ms, stddev: 2.129779 ms

With Percentage Feature - Release x86:

Stack with flex: median: 0.000000 ms, stddev: 0.132951 ms
Align stretch in undefined axis: median: 0.000000 ms, stddev: 0.136525 ms
Nested flex: median: 0.000000 ms, stddev: 0.489570 ms
Huge nested layout: median: 21.000000 ms, stddev: 1.390476 ms
Closes https://github.com/facebook/yoga/pull/258

Reviewed By: dshahidehpour

Differential Revision: D4361945

Pulled By: emilsjolander

fbshipit-source-id: a8f5bc63ad352eb9410d792729e56664468cd76a
2017-01-02 05:24:35 -08:00
Emil Sjolander
352f592767 Allow decimal measurements on java
Summary: Preserve floating point values when passing them across the JNI bridge.

Differential Revision: D4366605

fbshipit-source-id: 0b94ee87a03a6ed918360dd9998930e780fc865d
2016-12-29 04:55:56 -08:00
Emil Sjolander
0296511f2c YGNodeChildCount -> YGNodeGetChildCount for consistency
Summary: I kept wrongly typing this function which is a good sign that the name is inconsistent.

Reviewed By: gkassabli

Differential Revision: D4333480

fbshipit-source-id: 17058f18fa9e26b3e02f7a1651f7295cae59acad
2016-12-16 04:39:41 -08:00
Emil Sjolander
b11155423c Rename directories
Reviewed By: gkassabli

Differential Revision: D4284681

Summary: Rename csslayout directories to yoga

fbshipit-source-id: f0c6855c2c6e4389b7867f48f72cbb697830fc5a
2016-12-07 05:22:52 -08:00
Emil Sjolander
1b7ae2ed3d Update java package name to yoga
Summary: Update package name of java code to refer to yoga instead of csslayout. Still need to change the name of the folder where this code resides but that requires update github sync scripts etc so it is safer and easier to split these diffs apart.

Differential Revision: D4271420

fbshipit-source-id: b3cf150569a2331868410339cd19e5c694f2059e
2016-12-05 03:07:33 -08:00
Emil Sjolander
c6100d0771 Rename java API
Summary: Rename java api to new use yoga naming

Reviewed By: IanChilds

Differential Revision: D4265345

fbshipit-source-id: 69ecfd8fac214f86b8b70647b9b909acd83d78b5
2016-12-03 04:53:39 -08:00
Emil Sjolander
dda24b1e23 Rename C api
Summary: This renames the core C api to use the new Yoga branding.

Differential Revision: D4259190

fbshipit-source-id: 26c8b356ca464d4304f5f9dc4192bff10cea2dc9
2016-12-03 04:53:38 -08:00