summaryrefslogtreecommitdiffstats
path: root/Source
diff options
context:
space:
mode:
Diffstat (limited to 'Source')
-rw-r--r--Source/WebCore/Android.mk1
-rw-r--r--Source/WebCore/platform/graphics/android/GLWebViewState.cpp8
-rw-r--r--Source/WebCore/platform/graphics/android/GlyphMapAndroid.cpp67
-rw-r--r--Source/WebCore/platform/graphics/android/ImageBufferAndroid.cpp6
-rw-r--r--Source/WebCore/platform/graphics/android/TilesManager.cpp2
-rw-r--r--Source/WebCore/platform/graphics/android/VerticalTextMap.cpp104
-rw-r--r--Source/WebCore/platform/graphics/android/VerticalTextMap.h47
-rw-r--r--Source/WebKit/android/nav/CacheBuilder.cpp2
-rw-r--r--Source/WebKit/android/nav/CachedNode.cpp15
-rw-r--r--Source/WebKit/android/nav/CachedNode.h4
10 files changed, 231 insertions, 25 deletions
diff --git a/Source/WebCore/Android.mk b/Source/WebCore/Android.mk
index 5df4d93..a516f48 100644
--- a/Source/WebCore/Android.mk
+++ b/Source/WebCore/Android.mk
@@ -681,6 +681,7 @@ LOCAL_SRC_FILES := $(LOCAL_SRC_FILES) \
platform/graphics/android/TiledTexture.cpp \
platform/graphics/android/TransferQueue.cpp \
platform/graphics/android/UpdateManager.cpp \
+ platform/graphics/android/VerticalTextMap.cpp \
platform/graphics/android/VideoLayerAndroid.cpp \
platform/graphics/android/VideoLayerManager.cpp \
platform/graphics/android/ZoomManager.cpp \
diff --git a/Source/WebCore/platform/graphics/android/GLWebViewState.cpp b/Source/WebCore/platform/graphics/android/GLWebViewState.cpp
index 2d7b177..bf392578 100644
--- a/Source/WebCore/platform/graphics/android/GLWebViewState.cpp
+++ b/Source/WebCore/platform/graphics/android/GLWebViewState.cpp
@@ -296,10 +296,8 @@ void GLWebViewState::setViewport(SkRect& viewport, float scale)
// allocate max possible number of tiles visible with this viewport
int viewMaxTileX = static_cast<int>(ceilf((viewport.width()-1) * invTileContentWidth)) + 1;
int viewMaxTileY = static_cast<int>(ceilf((viewport.height()-1) * invTileContentHeight)) + 1;
-
- // NOTE: fetching 4 viewports worth, may need to be adjusted per-device
int maxTextureCount = (viewMaxTileX + TILE_PREFETCH_DISTANCE * 2) *
- (viewMaxTileY + TILE_PREFETCH_DISTANCE * 2) * 4;
+ (viewMaxTileY + TILE_PREFETCH_DISTANCE * 2) * 2;
TilesManager::instance()->setMaxTextureCount(maxTextureCount);
m_tiledPageA->updateBaseTileSize();
m_tiledPageB->updateBaseTileSize();
@@ -453,8 +451,10 @@ bool GLWebViewState::drawGL(IntRect& rect, SkRect& viewport, IntRect* invalRect,
// TODO: upload as many textures as possible within a certain time limit
bool ret = ImagesManager::instance()->uploadTextures();
- if (scale < MIN_SCALE_WARNING || scale > MAX_SCALE_WARNING)
+ if (scale < MIN_SCALE_WARNING || scale > MAX_SCALE_WARNING) {
XLOGC("WARNING, scale seems corrupted after update: %e", scale);
+ CRASH();
+ }
// gather the textures we can use
TilesManager::instance()->gatherLayerTextures();
diff --git a/Source/WebCore/platform/graphics/android/GlyphMapAndroid.cpp b/Source/WebCore/platform/graphics/android/GlyphMapAndroid.cpp
index da9d99a..c5193b7 100644
--- a/Source/WebCore/platform/graphics/android/GlyphMapAndroid.cpp
+++ b/Source/WebCore/platform/graphics/android/GlyphMapAndroid.cpp
@@ -32,6 +32,11 @@
#include "SkPaint.h"
#include "SkUtils.h"
#include "SimpleFontData.h"
+#include "Font.h"
+#include "SkFontHost.h"
+#include "HarfbuzzSkia.h"
+#include "VerticalTextMap.h"
+
using namespace android;
@@ -39,6 +44,36 @@ namespace WebCore {
#define NO_BREAK_SPACE_UNICHAR 0xA0
+static int substituteWithVerticalGlyphs(const FontPlatformData& platformData, uint16_t* glyphs, unsigned bufferLength)
+{
+ HB_FaceRec_* hbFace = platformData.harfbuzzFace();
+ if (!hbFace->gsub) {
+ // if there is no GSUB table, treat it as not covered
+ return 0Xffff;
+ }
+
+ HB_Buffer buffer;
+ hb_buffer_new(&buffer);
+ for (unsigned i = 0; i < bufferLength; ++i)
+ hb_buffer_add_glyph(buffer, glyphs[i], 0, i);
+
+ HB_UShort scriptIndex;
+ HB_UShort featureIndex;
+
+ HB_GSUB_Select_Script(hbFace->gsub, HB_MAKE_TAG('D', 'F', 'L', 'T'), &scriptIndex);
+ HB_GSUB_Select_Feature(hbFace->gsub, HB_MAKE_TAG('v', 'e', 'r', 't'), scriptIndex, 0xffff, &featureIndex);
+ HB_GSUB_Add_Feature(hbFace->gsub, featureIndex, 1);
+ HB_GSUB_Select_Feature(hbFace->gsub, HB_MAKE_TAG('v', 'r', 't', '2'), scriptIndex, 0xffff, &featureIndex);
+ HB_GSUB_Add_Feature(hbFace->gsub, featureIndex, 1);
+
+ int error = HB_GSUB_Apply_String(hbFace->gsub, buffer);
+ if (!error) {
+ for (unsigned i = 0; i < bufferLength; ++i)
+ glyphs[i] = static_cast<Glyph>(buffer->out_string[i].gindex);
+ }
+ return error;
+}
+
bool GlyphPage::fill(unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength, const SimpleFontData* fontData)
{
if (SkUTF16_IsHighSurrogate(buffer[bufferLength-1])) {
@@ -49,21 +84,47 @@ bool GlyphPage::fill(unsigned offset, unsigned length, UChar* buffer, unsigned b
SkPaint paint;
fontData->platformData().setupPaint(&paint);
paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
-
+
SkAutoSTMalloc <GlyphPage::size, uint16_t> glyphStorage(length);
uint16_t* glyphs = glyphStorage.get();
- unsigned count = paint.textToGlyphs(buffer, bufferLength << 1, glyphs);
+ UChar *textBuffer = buffer;
+ UChar vTextBuffer[bufferLength];
+
+ if (fontData->platformData().orientation() == Vertical &&
+ !fontData->hasVerticalGlyphs()) {
+ // Convert to vertical form if there is no vertical glyphs.
+ for (unsigned i = 0; i < bufferLength; ++i) {
+ vTextBuffer[i] = VerticalTextMap::getVerticalForm(buffer[i]);
+ if (!vTextBuffer[i])
+ vTextBuffer[i] = buffer[i];
+ }
+ textBuffer = vTextBuffer;
+ }
+
+ unsigned count = paint.textToGlyphs(textBuffer, bufferLength << 1, glyphs);
if (count != length) {
SkDebugf("%s count != length\n", __FUNCTION__);
return false;
}
+ if (fontData->hasVerticalGlyphs()) {
+ bool lookVariants = false;
+ for (unsigned i = 0; i < bufferLength; ++i) {
+ if (!Font::isCJKIdeograph(textBuffer[i])) {
+ lookVariants = true;
+ continue;
+ }
+ }
+ if (lookVariants)
+ substituteWithVerticalGlyphs(fontData->platformData(), glyphs, bufferLength);
+ }
+
unsigned allGlyphs = 0; // track if any of the glyphIDs are non-zero
// search for emoji. If we knew for sure that buffer was a contiguous range
// of chars, we could quick-reject the range to avoid this loop (usually)
if (EmojiFont::IsAvailable()) {
- const UChar* curr = buffer;
+ const UChar* curr = textBuffer;
for (unsigned i = 0; i < length; i++) {
SkUnichar uni = SkUTF16_NextUnichar(&curr);
uint16_t glyphID = glyphs[i];
diff --git a/Source/WebCore/platform/graphics/android/ImageBufferAndroid.cpp b/Source/WebCore/platform/graphics/android/ImageBufferAndroid.cpp
index bbde998..691fbca 100644
--- a/Source/WebCore/platform/graphics/android/ImageBufferAndroid.cpp
+++ b/Source/WebCore/platform/graphics/android/ImageBufferAndroid.cpp
@@ -85,11 +85,9 @@ PassRefPtr<Image> ImageBuffer::copyImage() const
SkDevice* device = canvas->getDevice();
const SkBitmap& orig = device->accessBitmap(false);
- if (!PlatformBridge::canSatisfyMemoryAllocation(orig.getSize()))
- return 0;
-
SkBitmap copy;
- orig.copyTo(&copy, orig.config());
+ if (PlatformBridge::canSatisfyMemoryAllocation(orig.getSize()))
+ orig.copyTo(&copy, orig.config());
SkBitmapRef* ref = new SkBitmapRef(copy);
RefPtr<Image> image = BitmapImage::create(ref, 0);
diff --git a/Source/WebCore/platform/graphics/android/TilesManager.cpp b/Source/WebCore/platform/graphics/android/TilesManager.cpp
index 7edc4b8..74cc764 100644
--- a/Source/WebCore/platform/graphics/android/TilesManager.cpp
+++ b/Source/WebCore/platform/graphics/android/TilesManager.cpp
@@ -69,7 +69,7 @@
// number to cap the layer tile texturs, it worked on both phones and tablets.
// TODO: after merge the pool of base tiles and layer tiles, we should revisit
// the logic of allocation management.
-#define MAX_TEXTURE_ALLOCATION ((6+TILE_PREFETCH_DISTANCE*2)*(5+TILE_PREFETCH_DISTANCE*2)*4)
+#define MAX_TEXTURE_ALLOCATION ((6+TILE_PREFETCH_DISTANCE*2)*(5+TILE_PREFETCH_DISTANCE*2)*2)
#define TILE_WIDTH 256
#define TILE_HEIGHT 256
#define LAYER_TILE_WIDTH 256
diff --git a/Source/WebCore/platform/graphics/android/VerticalTextMap.cpp b/Source/WebCore/platform/graphics/android/VerticalTextMap.cpp
new file mode 100644
index 0000000..2bb008f
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/VerticalTextMap.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2011, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "VerticalTextMap.h"
+
+#include <wtf/Forward.h>
+#include <wtf/MessageQueue.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefPtr.h>
+
+static const UChar vTextCnvTable[][2] = {
+ // TODO: uncomment mappings once we add glyphs for vertical forms.
+ // {0x0021, 0xfe15}, // exclamation mark
+ {0x0028, 0xfe35}, // left paren
+ {0x0029, 0xfe36}, // right paren
+ // {0x002c, 0xfe10}, // comma
+ {0x003a, 0xfe30}, // colon
+ {0x003b, 0x007c}, // hyphen
+ // {0x003f, 0xfe16}, // question mark
+ // {0x005b, 0xfe14}, // semicolon
+ {0x005d, 0xfe47}, // left square bracket
+ {0x005f, 0xfe48}, // right square bracket
+ {0x007b, 0xfe37}, // left curly bracket
+ {0x007d, 0xfe38}, // right curly bracket
+ {0x007e, 0x007c}, // tilde to vertical line
+ {0x2013, 0xfe32}, // en dash
+ {0x2014, 0xfe31}, // em dash
+ {0x2015, 0xfe31}, // horizontal bar
+ {0x2025, 0xfe30}, // two dot leader
+ // TODO: change the mapping 0x2026 -> 0xFE19 once Android has the glyph for 0xFE19.
+ {0x2026, 0xfe30}, // three dot leader
+ // {0x3001, 0xfe11}, // Ideographic comma
+ // {0x3002, 0xfe12}, // Ideographic full stop
+ {0x3008, 0xfe3f}, // left angle bracket
+ {0x3009, 0xfe40}, // right angle bracket
+ {0x300a, 0xfe3d}, // left double angle bracket
+ {0x300b, 0xfe3e}, // right double angle bracket
+ {0x300c, 0xfe41}, // left corner bracket
+ {0x300d, 0xfe42}, // right corner bracket
+ {0x300e, 0xfe43}, // left white corner bracket
+ {0x300f, 0xfe44}, // right white corner bracket
+ {0x3010, 0xfe3b}, // left black lenticular bracket
+ {0x3011, 0xfe3c}, // right black lenticular bracket
+ {0x3014, 0xfe39}, // left black lenticular bracket
+ {0x3015, 0xfe3a}, // right tortise shell bracket
+ // {0x3016, 0xfe17}, // left white lenticular bracket
+ // {0x3017, 0xfe18}, // right white lenticular bracket
+ // {0x3019, 0xfe19}, // horizontal ellipses
+ {0x30fc, 0x3021}, // prolonged sound
+ {0xfe4f, 0xfe34}, // wavy low line
+ {0xff08, 0xfe35}, // full width left paren
+ {0xff09, 0xfe36}, // full width right paren
+ {0xff3b, 0xfe47}, // full width left square bracket
+ {0xff3d, 0xfe48}, // full width right square bracket
+ {0xff5b, 0xfe37}, // full width left curly bracket
+ {0xff5d, 0xfe38}, // full width right curly bracket
+ // {0xff64, 0xfe11}, // halfwidth ideo comma
+ // {0xff61, 0xfe12}, // halfwidth ideo full stop
+};
+
+using namespace android;
+
+namespace WebCore {
+
+static WTF::Mutex m_verticalTextHashMapMutex;
+static HashMap<UChar, UChar>* verticalTextHashMap = 0;
+
+UChar VerticalTextMap::getVerticalForm(UChar c) {
+ {
+ MutexLocker lock(m_verticalTextHashMapMutex);
+ if (!verticalTextHashMap) {
+ // Lazy initialization.
+ verticalTextHashMap = new HashMap<UChar, UChar>;
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(vTextCnvTable); ++i)
+ verticalTextHashMap->set(vTextCnvTable[i][0], vTextCnvTable[i][1]);
+ }
+ }
+ return verticalTextHashMap->get(c);
+}
+
+}
diff --git a/Source/WebCore/platform/graphics/android/VerticalTextMap.h b/Source/WebCore/platform/graphics/android/VerticalTextMap.h
new file mode 100644
index 0000000..f614633
--- /dev/null
+++ b/Source/WebCore/platform/graphics/android/VerticalTextMap.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2011, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef android_VerticalTextMap_DEFINED
+#define android_VerticalTextMap_DEFINED
+
+#include "config.h"
+#include "WebViewCore.h"
+#include <wtf/StdLibExtras.h>
+#include <wtf/HashMap.h>
+#include <wtf/unicode/CharacterNames.h>
+
+using namespace android;
+
+namespace WebCore {
+ class VerticalTextMap {
+ public:
+ // This function converts given char to its corresponding vertical form.
+ // Rerturns 0 if there is no vertical form.
+ static UChar getVerticalForm(UChar c);
+ };
+}
+
+#endif
diff --git a/Source/WebKit/android/nav/CacheBuilder.cpp b/Source/WebKit/android/nav/CacheBuilder.cpp
index a4bc758..623d2cb 100644
--- a/Source/WebKit/android/nav/CacheBuilder.cpp
+++ b/Source/WebKit/android/nav/CacheBuilder.cpp
@@ -1406,7 +1406,6 @@ void CacheBuilder::BuildFrame(Frame* root, Frame* frame,
else if (cachedNode.clip(clip) == false)
continue; // skip this node if outside of the clip
}
- cachedNode.setNavableRects();
cachedNode.setColorIndex(colorIndex);
cachedNode.setExport(exported);
cachedNode.setHasCursorRing(hasCursorRing);
@@ -1478,7 +1477,6 @@ bool CacheBuilder::CleanUpContainedNodes(CachedRoot* cachedRoot,
lastNode->hasTagName(HTMLNames::formTag)) {
lastCached->setBounds(IntRect(0, 0, 0, 0));
lastCached->mCursorRing.clear();
- lastCached->setNavableRects();
return false;
}
CachedNode* onlyChildCached = cachedFrame->lastNode();
diff --git a/Source/WebKit/android/nav/CachedNode.cpp b/Source/WebKit/android/nav/CachedNode.cpp
index 86c2a38..e500875 100644
--- a/Source/WebKit/android/nav/CachedNode.cpp
+++ b/Source/WebKit/android/nav/CachedNode.cpp
@@ -92,10 +92,9 @@ void CachedNode::cursorRings(const CachedFrame* frame,
WebCore::IntRect CachedNode::cursorRingBounds(const CachedFrame* frame) const
{
- int partMax = mNavableRects;
- ASSERT(partMax > 0);
- WebCore::IntRect bounds = mCursorRing[0];
- for (int partIndex = 1; partIndex < partMax; partIndex++)
+ int partMax = navableRects();
+ WebCore::IntRect bounds;
+ for (int partIndex = 0; partIndex < partMax; partIndex++)
bounds.unite(mCursorRing[partIndex]);
bounds.inflate(CURSOR_RING_HIT_TEST_RADIUS);
return mIsInLayer ? frame->adjustBounds(this, bounds) : bounds;
@@ -116,7 +115,7 @@ void CachedNode::fixUpCursorRects(const CachedFrame* frame)
mUseHitBounds = true;
return;
}
- if (mNavableRects <= 1)
+ if (navableRects() <= 1)
return;
// if there is more than 1 rect, and the bounds doesn't intersect
// any other cursor ring bounds, use it
@@ -290,8 +289,8 @@ void CachedNode::move(int x, int y)
bool CachedNode::partRectsContains(const CachedNode* other) const
{
int outerIndex = 0;
- int outerMax = mNavableRects;
- int innerMax = other->mNavableRects;
+ int outerMax = navableRects();
+ int innerMax = other->navableRects();
do {
const WebCore::IntRect& outerBounds = mCursorRing[outerIndex];
int innerIndex = 0;
@@ -403,7 +402,7 @@ void CachedNode::Debug::print() const
DUMP_NAV_LOGD("// void* mParentGroup=%p; // (%d) \n", b->mParentGroup, mParentGroupIndex);
DUMP_NAV_LOGD("// int mDataIndex=%d;\n", b->mDataIndex);
DUMP_NAV_LOGD("// int mIndex=%d;\n", b->mIndex);
- DUMP_NAV_LOGD("// int mNavableRects=%d;\n", b->mNavableRects);
+ DUMP_NAV_LOGD("// int navableRects()=%d;\n", b->navableRects());
DUMP_NAV_LOGD("// int mParentIndex=%d;\n", b->mParentIndex);
DUMP_NAV_LOGD("// int mTabIndex=%d;\n", b->mTabIndex);
DUMP_NAV_LOGD("// int mColorIndex=%d;\n", b->mColorIndex);
diff --git a/Source/WebKit/android/nav/CachedNode.h b/Source/WebKit/android/nav/CachedNode.h
index 602dda6..321b7fd 100644
--- a/Source/WebKit/android/nav/CachedNode.h
+++ b/Source/WebKit/android/nav/CachedNode.h
@@ -136,7 +136,7 @@ public:
WebCore::IntRect localHitBounds(const CachedFrame* ) const;
WebCore::IntRect localRing(const CachedFrame* , size_t part) const;
void move(int x, int y);
- int navableRects() const { return mNavableRects; }
+ int navableRects() const { return mCursorRing.size(); }
void* nodePointer() const { return mNode; }
bool noSecondChance() const { return mCondition > SECOND_CHANCE_END; }
const WebCore::IntRect& originalAbsoluteBounds() const {
@@ -169,7 +169,6 @@ public:
void setIsTransparent(bool isTransparent) { mIsTransparent = isTransparent; }
void setIsUnclipped(bool unclipped) { mIsUnclipped = unclipped; }
void setLast() { mLast = true; }
- void setNavableRects() { mNavableRects = mCursorRing.size(); }
void setParentGroup(void* group) { mParentGroup = group; }
void setParentIndex(int parent) { mParentIndex = parent; }
void setSingleImage(bool single) { mSingleImage = single; }
@@ -195,7 +194,6 @@ private:
void* mParentGroup; // WebCore::Node*, only used to match pointers
int mDataIndex; // child frame if a frame; input data index; or -1
int mIndex; // index of itself, to find first in array (document)
- int mNavableRects; // FIXME: could be bitfield once I limit max number of rects
int mParentIndex;
int mTabIndex;
int mColorIndex; // index to ring color and other stylable properties