"Fossies" - the Fresh Open Source Software Archive

Member "incubator-pagespeed-mod-1.14.36.1/net/instaweb/rewriter/add_ids_filter.cc" (28 Feb 2020, 6315 Bytes) of package /linux/www/apache_httpd_modules/incubator-pagespeed-mod-1.14.36.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "add_ids_filter.cc" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.13.35.2_vs_1.14.36.1.

    1 /*
    2  * Licensed to the Apache Software Foundation (ASF) under one
    3  * or more contributor license agreements.  See the NOTICE file
    4  * distributed with this work for additional information
    5  * regarding copyright ownership.  The ASF licenses this file
    6  * to you under the Apache License, Version 2.0 (the
    7  * "License"); you may not use this file except in compliance
    8  * with the License.  You may obtain a copy of the License at
    9  * 
   10  *   http://www.apache.org/licenses/LICENSE-2.0
   11  * 
   12  * Unless required by applicable law or agreed to in writing,
   13  * software distributed under the License is distributed on an
   14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   15  * KIND, either express or implied.  See the License for the
   16  * specific language governing permissions and limitations
   17  * under the License.
   18  */
   19 
   20 
   21 #include "net/instaweb/rewriter/public/add_ids_filter.h"
   22 
   23 #include <algorithm>
   24 
   25 #include "base/logging.h"
   26 #include "pagespeed/kernel/base/basictypes.h"
   27 #include "pagespeed/kernel/base/string.h"
   28 #include "pagespeed/kernel/base/string_util.h"
   29 #include "pagespeed/kernel/html/html_element.h"
   30 #include "pagespeed/kernel/html/html_name.h"
   31 
   32 namespace net_instaweb {
   33 
   34 namespace {
   35 
   36 // TODO(jmaessen): perhaps this should go somewhere central?  It needs to be a
   37 // subset of the tags considered divlike by mobilize_label_filter at least.
   38 const HtmlName::Keyword kDivLikeTags[] = {
   39   HtmlName::kArticle,
   40   HtmlName::kAside,
   41   HtmlName::kContent,
   42   HtmlName::kDiv,
   43   HtmlName::kFooter,
   44   HtmlName::kForm,
   45   HtmlName::kHeader,
   46   HtmlName::kMain,
   47   HtmlName::kMenu,
   48   HtmlName::kNav,
   49   HtmlName::kSection,
   50   HtmlName::kTable,
   51   HtmlName::kTr,
   52   HtmlName::kUl
   53 };
   54 
   55 #ifndef NDEBUG
   56 // For invariant-checking the static data above.
   57 void CheckKeywordsSorted(const HtmlName::Keyword* list, int len) {
   58   for (int i = 1; i < len; ++i) {
   59     DCHECK_LT(list[i - 1], list[i]);
   60   }
   61 }
   62 #endif  // #ifndef NDEBUG
   63 
   64 bool IsDivLike(HtmlName::Keyword tag) {
   65   return std::binary_search(
   66       kDivLikeTags, kDivLikeTags + arraysize(kDivLikeTags), tag);
   67 }
   68 
   69 bool NeedsExplicitId(HtmlName::Keyword tag) {
   70   return IsDivLike(tag);
   71 }
   72 
   73 bool IsIgnored(HtmlName::Keyword tag) {
   74   return (tag == HtmlName::kHtml || tag == HtmlName::kBody);
   75 }
   76 
   77 }  // namespace
   78 
   79 // We don't want this to conflict with another id name, and length
   80 // also matters (shorter is better).
   81 const char AddIdsFilter::kIdPrefix[] = "PageSpeed";
   82 
   83 const int AddIdsFilter::kIsId = -1;
   84 
   85 AddIdsFilter::AddIdsFilter(RewriteDriver* driver)
   86     : driver_(driver) {}
   87 
   88 AddIdsFilter::~AddIdsFilter() {}
   89 
   90 void AddIdsFilter::StartDocument() {
   91   // Push an initial top-level count.
   92   div_count_stack_.clear();
   93   div_count_stack_.push_back(0);
   94   id_stack_.clear();
   95 #ifndef NDEBUG
   96   CheckKeywordsSorted(kDivLikeTags, arraysize(kDivLikeTags));
   97 #endif  // #ifndef NDEBUG
   98 }
   99 
  100 // As we parse outside head we maintain a stack of tag locations, and at each
  101 // tag for which TagRequiresId we add an encoded version of the stack as a query
  102 // param.  Note that the stack is incremented immediately after its encoded
  103 // value is added as a query param.
  104 //
  105 // An explicit id adds a kIsId entry to the stack before the entry for that
  106 // tag's children, and pushes the id onto the id_stack_.
  107 //
  108 // Example HTML:                   | Stack as we go:
  109 //                                 |
  110 // <html>                          | 0
  111 //   <head>                        | 0
  112 //   </head>                       | 0
  113 //   <body>                        | 0
  114 //     <div>                       | 0, 0 (id="...-0")
  115 //       <p>Toolbar link 1.</p>    | 0, 0
  116 //       <p>Toolbar link 2.</p>    | 0, 1
  117 //     </div>                      | 1             id stack
  118 //     <div id=foo>                | 1, -1, 0      foo
  119 //       <div>                     | 1, -1, 0, 0   foo (id="...-foo-0")
  120 //         <p>Main page link.</p>  | 1, -1, 0, 0   foo
  121 //       </div>                    | 1, 1
  122 //       <div>Secondary link.      | 1, -1, 1, 0   foo (id="...-foo-1")
  123 //       </div>                    | 1, -1, 2      foo
  124 //     </div>                      | 2
  125 //   </body>                       | 2
  126 // </html>                         | 2
  127 void AddIdsFilter::StartElement(HtmlElement* element) {
  128   HtmlName::Keyword tag = element->keyword();
  129   const HtmlElement::Attribute* id =
  130       element->FindAttribute(HtmlName::kId);
  131   if (id != NULL) {
  132     id_stack_.push_back(id);
  133     div_count_stack_.push_back(kIsId);
  134   } else if (IsIgnored(tag)) {
  135     // Don't touch stack in this case.
  136     return;
  137   } else if (NeedsExplicitId(tag) ||
  138              element->FindAttribute(HtmlName::kClass) != NULL) {
  139     driver_->AddAttribute(element, HtmlName::kId, GetDivCountStackEncoding());
  140   }
  141   div_count_stack_.push_back(0);
  142 }
  143 
  144 void AddIdsFilter::EndElement(HtmlElement* element) {
  145   DCHECK(!div_count_stack_.empty());
  146   DCHECK_NE(kIsId, div_count_stack_.back());
  147   if (!id_stack_.empty() &&
  148       id_stack_.back() == element->FindAttribute(HtmlName::kId)) {
  149     DCHECK_LT(2, div_count_stack_.size());
  150     // For an element with an id the stack looks like:
  151     // ... my_count_in_parent kIsId child_count
  152     // If so, pop both along with the back of id_stack_.
  153     div_count_stack_.pop_back();
  154     id_stack_.pop_back();
  155     // Now stack is ... my_count_in_parent kIsId
  156   } else if (IsIgnored(element->keyword())) {
  157     // Again, don't touch the stack in this case.
  158     return;
  159   } else {
  160     // stack is:
  161     // ... my_count_in_parent child_count
  162   }
  163   div_count_stack_.pop_back();
  164   // Stack is ... my_count_in_parent
  165   div_count_stack_.back()++;
  166   // Stack is ... my_count_in_parent+1
  167   DCHECK(!div_count_stack_.empty());
  168   DCHECK_NE(kIsId, div_count_stack_.back());
  169 }
  170 
  171 GoogleString AddIdsFilter::GetDivCountStackEncoding() {
  172   DCHECK(!div_count_stack_.empty());
  173   DCHECK_NE(kIsId, div_count_stack_.back());
  174   GoogleString result(kIdPrefix);
  175   if (!id_stack_.empty()) {
  176     // Note: we make use of StringPiece(NULL) -> "" in this call.
  177     StrAppend(&result, "-", id_stack_.back()->escaped_value());
  178   }
  179   int size = div_count_stack_.size();
  180   int count_index = size - 1;
  181   while (count_index > 0 && div_count_stack_[count_index - 1] != kIsId) {
  182     --count_index;
  183   }
  184   for (; count_index < size; ++count_index) {
  185     StrAppend(&result, "-", IntegerToString(div_count_stack_[count_index]));
  186   }
  187   return result;
  188 }
  189 
  190 }  // namespace net_instaweb