Skip to content
Browse files

[css] support for @media tags in CSS rules. Values supported include …

…iPad, ipad-retina, ipad-nonretina, iPhone, iphone-retina, iphone-nonretina, retina and nonretina. Tests added and working
  • Loading branch information...
1 parent 53cb2b6 commit c2e78b47d71931340b492c4602aff54059de6230 @djMax djMax committed Feb 27, 2013
View
8 src/Nimbus.xcodeproj/project.pbxproj
@@ -361,6 +361,7 @@
C7BBC6FE16DDC0E700833DC9 /* NITextField.h in Headers */ = {isa = PBXBuildFile; fileRef = C7BBC6B716DDC0DB00833DC9 /* NITextField.h */; };
C7BBC70316DDC12800833DC9 /* NITextField.h in Sources */ = {isa = PBXBuildFile; fileRef = C7BBC6B716DDC0DB00833DC9 /* NITextField.h */; };
C7BBC70416DDC12800833DC9 /* NITextField.m in Sources */ = {isa = PBXBuildFile; fileRef = C7BBC6B816DDC0DB00833DC9 /* NITextField.m */; };
+ C7BBC71116DE66BD00833DC9 /* media-rulesets.css in Resources */ = {isa = PBXBuildFile; fileRef = C7BBC71016DE66BD00833DC9 /* media-rulesets.css */; };
DB3A231713FD4B8E00614220 /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 66A03C1A13E6E85E00B514F3 /* SenTestingKit.framework */; };
DB3A231913FD4B8E00614220 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 66A03C0C13E6E85E00B514F3 /* Foundation.framework */; };
DB3A231D13FD4B8E00614220 /* libNimbusAttributedLabel.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DB3A230913FD4B8E00614220 /* libNimbusAttributedLabel.a */; };
@@ -781,7 +782,7 @@
66832CA9143D6642003E413C /* CSSTokens.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = CSSTokens.cpp; path = css/grammar/CSSTokens.cpp; sourceTree = SOURCE_ROOT; };
66832CAA143D6642003E413C /* CSSTokens.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = CSSTokens.h; path = css/grammar/CSSTokens.h; sourceTree = SOURCE_ROOT; };
66832CB7143D681B003E413C /* NimbusCSS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NimbusCSS.h; path = css/src/NimbusCSS.h; sourceTree = SOURCE_ROOT; };
- 66832CC0143D7883003E413C /* NICSSParserTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NICSSParserTests.m; path = css/unittests/NICSSParserTests.m; sourceTree = SOURCE_ROOT; };
+ 66832CC0143D7883003E413C /* NICSSParserTests.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.objc; name = NICSSParserTests.m; path = css/unittests/NICSSParserTests.m; sourceTree = SOURCE_ROOT; tabWidth = 4; };
66832CC2143D7898003E413C /* NICSSParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NICSSParser.h; path = css/src/NICSSParser.h; sourceTree = SOURCE_ROOT; };
66832CC3143D7898003E413C /* NICSSParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NICSSParser.m; path = css/src/NICSSParser.m; sourceTree = SOURCE_ROOT; };
66832CC9143D7994003E413C /* NimbusCSSTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "NimbusCSSTests-Info.plist"; path = "css/unittests/NimbusCSSTests-Info.plist"; sourceTree = SOURCE_ROOT; };
@@ -993,6 +994,7 @@
C7BBC6B716DDC0DB00833DC9 /* NITextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NITextField.h; sourceTree = "<group>"; };
C7BBC6B816DDC0DB00833DC9 /* NITextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NITextField.m; sourceTree = "<group>"; };
C7BBC70216DDC0E700833DC9 /* libNimbusTextField.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libNimbusTextField.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ C7BBC71016DE66BD00833DC9 /* media-rulesets.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; name = "media-rulesets.css"; path = "css/unittests/media-rulesets.css"; sourceTree = SOURCE_ROOT; };
DB3A230913FD4B8E00614220 /* libNimbusAttributedLabel.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libNimbusAttributedLabel.a; sourceTree = BUILT_PRODUCTS_DIR; };
DB3A231613FD4B8E00614220 /* NimbusAttributedLabelTests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NimbusAttributedLabelTests.octest; sourceTree = BUILT_PRODUCTS_DIR; };
DB3A233213FD4BE500614220 /* NIAttributedLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NIAttributedLabel.h; sourceTree = "<group>"; };
@@ -1583,6 +1585,7 @@
66FCC632144FB42E0029F1A6 /* includee.css */,
66FCC633144FB42E0029F1A6 /* includer.css */,
66832CC9143D7994003E413C /* NimbusCSSTests-Info.plist */,
+ C7BBC71016DE66BD00833DC9 /* media-rulesets.css */,
);
name = resources;
sourceTree = "<group>";
@@ -1674,7 +1677,9 @@
664EFAD6155989D9009826AB /* Examples */,
66A03C0A13E6E85E00B514F3 /* Products */,
);
+ indentWidth = 2;
sourceTree = "<group>";
+ tabWidth = 2;
};
66A03C0A13E6E85E00B514F3 /* Products */ = {
isa = PBXGroup;
@@ -3077,6 +3082,7 @@
66832CFF143E3294003E413C /* UILabel.css in Resources */,
66FCC634144FB42E0029F1A6 /* includee.css in Resources */,
66FCC635144FB42E0029F1A6 /* includer.css in Resources */,
+ C7BBC71116DE66BD00833DC9 /* media-rulesets.css in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
View
2 src/css/grammar/CSSTokens.h
@@ -35,7 +35,7 @@ typedef enum {
CSSUNICODERANGE,
CSSIMPORT,
CSSUNKNOWN,
-
+ CSSMEDIA
} CssParserCodes;
extern const char* cssnames[];
View
2 src/css/grammar/css.grammar
@@ -37,7 +37,7 @@ range \?{1,6}|{h}(\?{0,5}|{h}(\?{0,4}|{h}(\?{0,3}|{h}(\?{0,2}|{h}(\??|{h})))
"@import" {cssConsume(yytext, CSSIMPORT);}
"@page"
-"@media"
+"@media" {cssConsume(yytext, CSSMEDIA);}
"@font-face"
"@charset"
"@namespace"
View
23 src/css/src/CSSTokenizer.m
@@ -84,6 +84,7 @@
typedef uint16_t flex_uint16_t;
typedef int32_t flex_int32_t;
typedef uint32_t flex_uint32_t;
+typedef uint64_t flex_uint64_t;
#else
typedef signed char flex_int8_t;
typedef short int flex_int16_t;
@@ -392,7 +393,7 @@
*/
#define YY_DO_BEFORE_ACTION \
(yytext_ptr) = yy_bp; \
- cssleng = (size_t) (yy_cp - yy_bp); \
+ cssleng = (yy_size_t) (yy_cp - yy_bp); \
(yy_hold_char) = *yy_cp; \
*yy_cp = '\0'; \
(yy_c_buf_p) = yy_cp;
@@ -2097,7 +2098,7 @@
#define YY_RESTORE_YY_MORE_OFFSET
char *csstext;
#line 1 "css.grammar"
-#line 2082 "lex.css.c"
+#line 2083 "lex.css.c"
#define INITIAL 0
@@ -2280,7 +2281,7 @@
#line 21 "css.grammar"
-#line 2265 "lex.css.c"
+#line 2266 "lex.css.c"
if ( !(yy_init) )
{
@@ -2426,7 +2427,7 @@
case 12:
YY_RULE_SETUP
#line 40 "css.grammar"
-
+{cssConsume(csstext, CSSMEDIA);}
YY_BREAK
case 13:
YY_RULE_SETUP
@@ -2577,7 +2578,7 @@
#line 75 "css.grammar"
ECHO;
YY_BREAK
-#line 2562 "lex.css.c"
+#line 2563 "lex.css.c"
case YY_STATE_EOF(INITIAL):
yyterminate();
@@ -3021,6 +3022,8 @@ void css_switch_to_buffer (YY_BUFFER_STATE new_buffer )
YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
}
+
+ // This line looks to be a manual fix from flex output... Restore if you regenerate the file.
if (yy_buffer_stack) {
YY_CURRENT_BUFFER_LVALUE = new_buffer;
}
@@ -3175,10 +3178,11 @@ void csspush_buffer_state (YY_BUFFER_STATE new_buffer )
/* Only push if top exists. Otherwise, replace top. */
if (YY_CURRENT_BUFFER)
(yy_buffer_stack_top)++;
+ // This line looks to be a manual fix from flex output... Restore if you regenerate the file.
if (yy_buffer_stack) {
YY_CURRENT_BUFFER_LVALUE = new_buffer;
}
-
+
/* copied from css_switch_to_buffer. */
css_load_buffer_state( );
(yy_did_buffer_switch_on_eof) = 1;
@@ -3222,6 +3226,13 @@ static void cssensure_buffer_stack (void)
const yy_size_t size_to_alloc = ptr_size /* * num_to_alloc */;
(yy_buffer_stack) = (struct yy_buffer_state**)cssalloc(size_to_alloc);
+ /* flex generates this, but the above 4 lines were here before, so I'm keeping them as is.
+ * it would sure seem like num_to_alloc isn't getting set as it should in the above
+ (yy_buffer_stack) = (struct yy_buffer_state**)cssalloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+ */
+
if ( ! (yy_buffer_stack) )
YY_FATAL_ERROR( "out of dynamic memory in cssensure_buffer_stack()" );
View
2 src/css/src/CSSTokens.h
@@ -35,7 +35,7 @@ typedef enum {
CSSUNICODERANGE,
CSSIMPORT,
CSSUNKNOWN,
-
+ CSSMEDIA
} CssParserCodes;
extern const char* cssnames[];
View
3 src/css/src/NICSSParser.h
@@ -44,12 +44,15 @@ extern NSString* const kDependenciesSelectorKey;
NSMutableArray* _scopesForActiveRuleset;
NSString* _currentPropertyName;
NSMutableArray* _importedFilenames;
+ BOOL droppingCurrentRules;
union {
struct {
int InsideRuleset : 1; // Within `ruleset {...}`
int InsideProperty : 1; // Defining a `property: ...`
int InsideFunction : 1; // Within a `function(...)`
+ int InsideMedia : 1; // within `@media {}`
+ int ReadingMedia : 1; // got @media start, waiting for rules and a brace
} Flags;
int _data;
} _state;
View
103 src/css/src/NICSSParser.m
@@ -92,10 +92,47 @@ - (void)consumeToken:(int)token text:(char*)text {
NSString* lowercaseTextAsString = [textAsString lowercaseString];
switch (token) {
+ case CSSMEDIA: // @media { }
+ if (_state.Flags.InsideMedia || _state.Flags.ReadingMedia) {
+ [self setFailFlag];
+ }
+ _state.Flags.ReadingMedia = YES;
+ droppingCurrentRules = YES; // at least one must match to undo this
+ break;
case CSSHASH: // #{name}
case CSSIDENT: { // {ident}(:{ident})?
- if (_state.Flags.InsideRuleset) {
+ if (_state.Flags.ReadingMedia) {
+ if (!droppingCurrentRules) {
+ // No point in running these checks if we've already decided
+ break;
+ } else if ([textAsString caseInsensitiveCompare:@"ipad"] == NSOrderedSame) {
+ if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) { droppingCurrentRules = NO; }
+ break;
+ } else if ([textAsString caseInsensitiveCompare:@"iphone"] == NSOrderedSame) {
+ if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone) { droppingCurrentRules = NO; }
+ break;
+ } else if ([textAsString caseInsensitiveCompare:@"retina"] == NSOrderedSame) {
+ if ([UIScreen mainScreen].scale != 1.0) { droppingCurrentRules = NO; }
+ break;
+ } else if ([textAsString caseInsensitiveCompare:@"nonretina"] == NSOrderedSame) {
+ if ([UIScreen mainScreen].scale == 1.0) { droppingCurrentRules = NO; }
+ break;
+ } else if ([textAsString caseInsensitiveCompare:@"ipad-retina"] == NSOrderedSame) {
+ if ([UIScreen mainScreen].scale != 1.0 && [UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) { droppingCurrentRules = NO; }
+ break;
+ } else if ([textAsString caseInsensitiveCompare:@"ipad-nonretina"] == NSOrderedSame) {
+ if ([UIScreen mainScreen].scale == 1.0 && [UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) { droppingCurrentRules = NO; }
+ break;
+ } else if ([textAsString caseInsensitiveCompare:@"iphone-retina"] == NSOrderedSame) {
+ if ([UIScreen mainScreen].scale != 1.0 && [UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone) { droppingCurrentRules = NO; }
+ break;
+ } else if ([textAsString caseInsensitiveCompare:@"iphone-nonretina"] == NSOrderedSame) {
+ if ([UIScreen mainScreen].scale == 1.0 && [UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone) { droppingCurrentRules = NO; }
+ break;
+ }
+ }
+ else if (_state.Flags.InsideRuleset) {
NIDASSERT(nil != _mutatingRuleset);
if (nil == _mutatingRuleset) {
[self setFailFlag];
@@ -190,14 +227,21 @@ - (void)consumeToken:(int)token text:(char*)text {
// Commit the current selector and start a new one.
case ',': {
- if (!_state.Flags.InsideRuleset) {
+ if (_state.Flags.ReadingMedia) {
+ // Ignore, more media coming
+ } else if (!_state.Flags.InsideRuleset) {
[self commitCurrentSelector];
}
break;
}
// Start a new rule set.
case '{': {
+ if (_state.Flags.ReadingMedia) {
+ _state.Flags.InsideMedia = YES;
+ _state.Flags.ReadingMedia = NO;
+ break;
+ }
NIDASSERT(nil != _mutatingScope);
if ([_mutatingScope count] > 0
&& !_state.Flags.InsideRuleset && !_state.Flags.InsideFunction) {
@@ -217,36 +261,45 @@ - (void)consumeToken:(int)token text:(char*)text {
// Commit an existing rule set.
case '}': {
- for (NSString* name in _scopesForActiveRuleset) {
- NSMutableDictionary* existingProperties = [_rulesets objectForKey:name];
-
- if (nil == existingProperties) {
- NSMutableDictionary* ruleSet = [_mutatingRuleset mutableCopy];
- [_rulesets setObject:ruleSet forKey:name];
-
- } else {
- // Properties already exist, so overwrite them.
- // Merge the orders.
- {
- NSMutableArray* order = [existingProperties objectForKey:kPropertyOrderKey];
- [order addObjectsFromArray:[_mutatingRuleset objectForKey:kPropertyOrderKey]];
- [_mutatingRuleset setObject:order forKey:kPropertyOrderKey];
- }
-
- for (NSString* key in _mutatingRuleset) {
- [existingProperties setObject:[_mutatingRuleset objectForKey:key] forKey:key];
+
+ if (_state.Flags.InsideMedia && !_mutatingRuleset) {
+ // End of a media tag
+ _state.Flags.InsideMedia = NO;
+ droppingCurrentRules = NO;
+ } else {
+ if (!droppingCurrentRules) {
+ for (NSString* name in _scopesForActiveRuleset) {
+ NSMutableDictionary* existingProperties = [_rulesets objectForKey:name];
+
+ if (nil == existingProperties) {
+ NSMutableDictionary* ruleSet = [_mutatingRuleset mutableCopy];
+ [_rulesets setObject:ruleSet forKey:name];
+
+ } else {
+ // Properties already exist, so overwrite them.
+ // Merge the orders.
+ {
+ NSMutableArray* order = [existingProperties objectForKey:kPropertyOrderKey];
+ [order addObjectsFromArray:[_mutatingRuleset objectForKey:kPropertyOrderKey]];
+ [_mutatingRuleset setObject:order forKey:kPropertyOrderKey];
+ }
+
+ for (NSString* key in _mutatingRuleset) {
+ [existingProperties setObject:[_mutatingRuleset objectForKey:key] forKey:key];
+ }
+ // Add the order of the new properties.
+ NSMutableArray* order = [existingProperties objectForKey:kPropertyOrderKey];
+ [order addObjectsFromArray:[_mutatingRuleset objectForKey:kPropertyOrderKey]];
+ }
}
- // Add the order of the new properties.
- NSMutableArray* order = [existingProperties objectForKey:kPropertyOrderKey];
- [order addObjectsFromArray:[_mutatingRuleset objectForKey:kPropertyOrderKey]];
}
- }
-
+
_mutatingRuleset = nil;
[_scopesForActiveRuleset removeAllObjects];
_state.Flags.InsideRuleset = NO;
_state.Flags.InsideProperty = NO;
_state.Flags.InsideFunction = NO;
+ }
break;
}
View
41 src/css/unittests/NICSSParserTests.m
@@ -115,6 +115,47 @@ - (void)testEmptyRulesets {
}
}
+///////////////////////////////////////////////////////////////////////////////////////////////////
+- (void)testMediaRulesets {
+ NICSSParser* parser = [[NICSSParser alloc] init];
+
+ NSString* pathToFile = NIPathForBundleResource(_unitTestBundle, @"media-rulesets.css");
+
+ NSDictionary* rulesets = [parser dictionaryForPath:pathToFile];
+ // STAssertNotNil([rulesets objectForKey:@"UIView"], @"@media tag with all known combinations didn't match one.");
+ if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone) {
+ if ([UIScreen mainScreen].scale == 1.0) {
+ STAssertNotNil([rulesets objectForKey:@"UINavigationBar"], @"@media tag didn't match properly.");
+ STAssertNotNil([rulesets objectForKey:@"#UINavigationBar"], @"@media tag didn't match properly.");
+ STAssertNil([rulesets objectForKey:@"#UITextField"], @"@media tag shouldn't have matched.");
+ STAssertNil([rulesets objectForKey:@"UIButton"], @"@media tag shouldn't have matched.");
+ STAssertNil([rulesets objectForKey:@"#UIButton"], @"@media tag shouldn't have matched.");
+ STAssertNil([rulesets objectForKey:@"#UILabel"], @"@media tag shouldn't have matched.");
+ } else {
+ STAssertNotNil([rulesets objectForKey:@"UINavigationBar"], @"@media tag didn't match properly.");
+ STAssertNotNil([rulesets objectForKey:@"#UITextField"], @"@media tag didn't match properly.");
+ STAssertNil([rulesets objectForKey:@"#UINavigationBar"], @"@media tag shouldn't have matched.");
+ STAssertNil([rulesets objectForKey:@"UIButton"], @"@media tag shouldn't have matched.");
+ STAssertNil([rulesets objectForKey:@"#UIButton"], @"@media tag shouldn't have matched.");
+ STAssertNil([rulesets objectForKey:@"#UILabel"], @"@media tag shouldn't have matched.");
+ }
+ } else {
+ if ([UIScreen mainScreen].scale == 1.0) {
+ STAssertNotNil([rulesets objectForKey:@"UIButton"], @"@media tag didn't match properly.");
+ STAssertNotNil([rulesets objectForKey:@"#UIButton"], @"@media tag didn't match properly.");
+ STAssertNil([rulesets objectForKey:@"UINavigationBar"], @"@media tag shouldn't have matched.");
+ STAssertNil([rulesets objectForKey:@"#UINavigationBar"], @"@media tag shouldn't have matched.");
+ STAssertNil([rulesets objectForKey:@"#UITextField"], @"@media tag shouldn't have matched.");
+ STAssertNil([rulesets objectForKey:@"#UILabel"], @"@media tag shouldn't have matched.");
+ } else {
+ STAssertNotNil([rulesets objectForKey:@"UIButton"], @"@media tag didn't match properly.");
+ STAssertNotNil([rulesets objectForKey:@"#UILabel"], @"@media tag didn't match properly.");
+ STAssertNil([rulesets objectForKey:@"UINavigationBar"], @"@media tag shouldn't have matched.");
+ STAssertNil([rulesets objectForKey:@"#UINavigationBar"], @"@media tag shouldn't have matched.");
+ STAssertNil([rulesets objectForKey:@"#UITextField"], @"@media tag shouldn't have matched.");
+ }
+ }
+}
///////////////////////////////////////////////////////////////////////////////////////////////////
- (void)testRulesets {
View
49 src/css/unittests/media-rulesets.css
@@ -0,0 +1,49 @@
+@media iPad-retina,iPad-nonretina,iPhone-retina,iPhone-nonretina {
+UIView {
+}
+}
+
+@media iPad
+{
+UIButton {
+}
+}
+
+@media iPhone
+{
+UINavigationBar {
+}
+}
+
+@media nonRetina {
+UILabel {
+}
+}
+
+@media retina {
+UITextField {
+}
+}
+
+@media iPad-nonRetina
+{
+#UIButton {
+}
+}
+
+@media iPhone-nonRetina
+{
+#UINavigationBar {
+}
+}
+
+@media iPad-retina {
+#UILabel {
+}
+}
+
+@media iPhone-retina {
+#UITextField {
+}
+}
+

0 comments on commit c2e78b4

Please sign in to comment.
Something went wrong with that request. Please try again.