이전에 작성한 linkElement, style element에 이어 media rule을 사용해 media query를 주었을 때 웹킷이 처리하는 과정을 정리해보고자 합니다.
이전글 1 : Media query in webkit(link Element)
이전글 2 : Media query in webkit(style element)
특히 이전글2는 반드시 읽으셔야 합니다.
사용할 예제는 이전글2에서 잠깐 보여드렸던 것과 동일합니다.
### html
<!doctype html>
<head>
<style>
@media screen and (min-width:300px) {
body { background-color:red; }
}
</style>
<body>
hello
</body>
시작 지점은 이전 예제의 밑에서 두번째 코드인 DocumentRuleSets::appendAuthorStyleSheets 입니다.
### c++ ; highlight : 21
void DocumentRuleSets::appendAuthorStyleSheets(unsigned firstNew, const Vector<RefPtr<CSSStyleSheet> >& styleSheets, MediaQueryEvaluator* medium, InspectorCSSOMWrappers& inspectorCSSOMWrappers, bool isViewSource, StyleResolver* resolver)
{
// This handles sheets added to the end of the stylesheet list only. In other cases the style resolver
// needs to be reconstructed. To handle insertions too the rule order numbers would need to be updated.
unsigned size = styleSheets.size();
for (unsigned i = firstNew; i < size; ++i) {
CSSStyleSheet* cssSheet = styleSheets[i].get();
ASSERT(!cssSheet->disabled());
if (cssSheet->mediaQueries() && !medium->eval(cssSheet->mediaQueries(), resolver))
continue;
StyleSheetContents* sheet = cssSheet->contents();
#if ENABLE(STYLE_SCOPED) || ENABLE(SHADOW_DOM)
if (const ContainerNode* scope = StyleScopeResolver::scopeFor(cssSheet)) {
// FIXME: Remove a dependency to calling a StyleResolver's member function.
// If we can avoid calling resolver->ensureScopeResolver() here, we don't have to include "StyleResolver.h".
// https://bugs.webkit.org/show_bug.cgi?id=108890
resolver->ensureScopeResolver()->ensureRuleSetFor(scope)->addRulesFromSheet(sheet, *medium, resolver, scope);
continue;
}
#endif
m_authorStyle->addRulesFromSheet(sheet, *medium, resolver);
inspectorCSSOMWrappers.collectFromStyleSheetIfNeeded(cssSheet);
}
m_authorStyle->shrinkToFit();
collectFeatures(isViewSource, resolver->scopeResolver());
}
현재 styleSheet에 mediaQuery가 있다면, 2번글에서와 같이 eval과정을 거쳐서 addRulesFromSheet를 수행할지 말지를 결정할 것입니다. [9, 21라인]
하지만, 이번 예제에서 stylesheet에는 mediaQuery가 없습니다. 따라서 addRulesFromSheet가 수행됩니다.
addRulesFromSheet는 다음과 같습니다.
### c++
void RuleSet::addRulesFromSheet(StyleSheetContents* sheet, const MediaQueryEvaluator& medium, StyleResolver* resolver, const ContainerNode* scope)
{
ASSERT(sheet);
const Vector<RefPtr<StyleRuleImport> >& importRules = sheet->importRules();
for (unsigned i = 0; i < importRules.size(); ++i) {
StyleRuleImport* importRule = importRules[i].get();
if (importRule->styleSheet() && (!importRule->mediaQueries() || medium.eval(importRule->mediaQueries(), resolver)))
addRulesFromSheet(importRule->styleSheet(), medium, resolver, scope);
}
bool hasDocumentSecurityOrigin = resolver && resolver->document()->securityOrigin()->canRequest(sheet->baseURL());
AddRuleFlags addRuleFlags = static_cast<AddRuleFlags>((hasDocumentSecurityOrigin ? RuleHasDocumentSecurityOrigin : 0) | (!scope ? RuleCanUseFastCheckSelector : 0));
addChildRules(sheet->childRules(), medium, resolver, scope, hasDocumentSecurityOrigin, addRuleFlags);
if (m_autoShrinkToFitEnabled)
shrinkToFit();
}
sheet의 import rule들을 모두 순회하며, import rule의 media query를 확인, 있으면, 해당 import rule의 styleSheet를 위해 addRulesFromSheet를 호출합니다.
여기서는 import rule도 없으므로 지나가면 addChildRules라는 함수에서 실제 childRules들을 반영합니다. 이 함수의 인자에도 medium이 있는 것을 볼 수 있습니다.
### c++ ; highlight : [11, 13]
void RuleSet::addChildRules(const Vector<RefPtr<StyleRuleBase> >& rules, const MediaQueryEvaluator& medium, StyleResolver* resolver, const ContainerNode* scope, bool hasDocumentSecurityOrigin, AddRuleFlags addRuleFlags)
{
for (unsigned i = 0; i < rules.size(); ++i) {
StyleRuleBase* rule = rules[i].get();
if (rule->isStyleRule()) {
StyleRule* styleRule = static_cast<StyleRule*>(rule);
addStyleRule(styleRule, addRuleFlags);
} else if (rule->isPageRule())
addPageRule(static_cast<StyleRulePage*>(rule));
else if (rule->isMediaRule()) {
StyleRuleMedia* mediaRule = static_cast<StyleRuleMedia*>(rule);
if ((!mediaRule->mediaQueries() || medium.eval(mediaRule->mediaQueries(), resolver)))
addChildRules(mediaRule->childRules(), medium, resolver, scope, hasDocumentSecurityOrigin, addRuleFlags);
} else if (rule->isFontFaceRule() && resolver) {
// Add this font face to our set.
// FIXME(BUG 72461): We don't add @font-face rules of scoped style sheets for the moment.
if (scope)
continue;
const StyleRuleFontFace* fontFaceRule = static_cast<StyleRuleFontFace*>(rule);
resolver->fontSelector()->addFontFaceRule(fontFaceRule);
resolver->invalidateMatchedPropertiesCache();
} else if (rule->isKeyframesRule() && resolver) {
// FIXME (BUG 72462): We don't add @keyframe rules of scoped style sheets for the moment.
if (scope)
continue;
resolver->addKeyframeStyle(static_cast<StyleRuleKeyframes*>(rule));
}
#if ENABLE(CSS_REGIONS)
else if (rule->isRegionRule() && resolver) {
// FIXME (BUG 72472): We don't add @-webkit-region rules of scoped style sheets for the moment.
if (scope)
continue;
addRegionRule(static_cast<StyleRuleRegion*>(rule), hasDocumentSecurityOrigin);
}
#endif
#if ENABLE(SHADOW_DOM)
else if (rule->isHostRule())
resolver->addHostRule(static_cast<StyleRuleHost*>(rule), hasDocumentSecurityOrigin, scope);
#endif
#if ENABLE(CSS_DEVICE_ADAPTATION)
else if (rule->isViewportRule() && resolver) {
// @viewport should not be scoped.
if (scope)
continue;
resolver->viewportStyleResolver()->addViewportRule(static_cast<StyleRuleViewport*>(rule));
}
#endif
#if ENABLE(CSS3_CONDITIONAL_RULES)
else if (rule->isSupportsRule() && static_cast<StyleRuleSupports*>(rule)->conditionIsSupported())
addChildRules(static_cast<StyleRuleSupports*>(rule)->childRules(), medium, resolver, scope, hasDocumentSecurityOrigin, addRuleFlags);#endif
}
}
다양한 CSS rule들을 처리하는 코드 가운데 media rule에 관한 코드를 발견하실 수 있습니다. [11라인]
media query를 발생시키는 코드들은 다 살펴보았지만, 2번글 마지막에서 잠깐 언급했던 것과 같이, viewport와 관련된 media query는 화면의 크기등이 바뀌었을 때 다시 계산할 필요가 있습니다.
예를 들어 webview를 resize하는 경우, FrameView의 size가 변경되어 FrameView::setFrameRect가 호출될 것입니다.
### c++ ; highlight : [33, 34, 35, 36, 37]
void FrameView::setFrameRect(const IntRect& newRect)
{
IntRect oldRect = frameRect();
if (newRect == oldRect)
return;
#if ENABLE(TEXT_AUTOSIZING)
// Autosized font sizes depend on the width of the viewing area.
if (newRect.width() != oldRect.width()) {
Page* page = m_frame ? m_frame->page() : 0;
if (page && page->mainFrame() == m_frame && page->settings()->textAutosizingEnabled()) {
for (Frame* frame = page->mainFrame(); frame; frame = frame->tree()->traverseNext())
m_frame->document()->textAutosizer()->recalculateMultipliers();
}
}
#endif
ScrollView::setFrameRect(newRect);
updateScrollableAreaSet();
RenderView* renderView = this->renderView();
#if USE(ACCELERATED_COMPOSITING)
if (renderView) {
if (renderView->usesCompositing())
renderView->compositor()->frameViewDidChangeSize();
}
#endif
Document* document = m_frame ? m_frame->document() : 0;
// Viewport-dependent media queries may cause us to need completely different style information.
if (document && document->styleResolverIfExists() && document->styleResolverIfExists()->affectedByViewportChange()) {
document->styleResolverChanged(DeferRecalcStyle);
InspectorInstrumentation::mediaQueryResultChanged(document);
}
sendResizeEventIfNeeded();
}
document객체에 있는 styleResolver의 affectedByViewportChange를 통해 media query의 결과가 바뀌었는지 보고[34 라인],
바뀌었다면, styleResolverChanged를 다시 불러줍니다.[35라인]
affectedByViewportChange는 아래와 같습니다.
### c++
bool StyleResolver::affectedByViewportChange() const
{
unsigned s = m_viewportDependentMediaQueryResults.size();
for (unsigned i = 0; i < s; i++) {
if (m_medium->eval(&m_viewportDependentMediaQueryResults[i]->m_expression) != m_viewportDependentMediaQueryResults[i]->m_result)
return true;
}
return false;
}
'Open Source > webkit/chromium' 카테고리의 다른 글
WebKit/Efl 빌드하기(우분투) (2) | 2013.11.26 |
---|---|
[chrome] class-diagram-webkit-webcore-to-chrome-browser (0) | 2013.07.05 |
ewebkit package 09/04/22 (0) | 2013.03.25 |
[code reading] Media query in webkit(style element) (0) | 2013.03.08 |
[code reading] Media query in webkit (linkElement) (1) | 2013.03.06 |