blob: f2b8f64bfdb93666165dbc0cff75bbfe9da0b7b4 [file] [log] [blame]
/*
* Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Author: jmarantz@google.com (Joshua Marantz)
#ifndef NET_INSTAWEB_REWRITER_PUBLIC_RESOURCE_SLOT_H_
#define NET_INSTAWEB_REWRITER_PUBLIC_RESOURCE_SLOT_H_
#include <set>
#include <vector>
#include "net/instaweb/rewriter/public/resource.h"
#include "pagespeed/kernel/base/basictypes.h"
#include "pagespeed/kernel/base/ref_counted_ptr.h"
#include "pagespeed/kernel/base/string.h"
#include "pagespeed/kernel/base/string_util.h"
#include "pagespeed/kernel/base/vector_deque.h"
#include "pagespeed/kernel/html/html_element.h"
#include "pagespeed/kernel/http/google_url.h"
namespace net_instaweb {
class CachedResults;
class HtmlResourceSlot;
class ResourceSlot;
class RewriteContext;
class RewriteDriver;
class RewriteOptions;
typedef RefCountedPtr<ResourceSlot> ResourceSlotPtr;
typedef RefCountedPtr<HtmlResourceSlot> HtmlResourceSlotPtr;
typedef std::vector<ResourceSlotPtr> ResourceSlotVector;
// A slot is a place in a web-site resource a URL is found, and may be
// rewritten. Types of slots include HTML element attributes and CSS
// background URLs. In principle they could also include JS ajax
// requests, although this is NYI.
//
// TODO(jmarantz): make this class thread-safe.
class ResourceSlot : public RefCounted<ResourceSlot> {
public:
explicit ResourceSlot(const ResourcePtr& resource)
: resource_(resource),
preserve_urls_(false),
disable_rendering_(false),
should_delete_element_(false),
disable_further_processing_(false),
was_optimized_(false) {
}
ResourcePtr resource() const { return resource_; }
// Return HTML element associated with slot, or NULL if none (CSS, IPRO)
virtual HtmlElement* element() const = 0;
// Note that while slots can be mutated by multiple threads; they are
// implemented with thread-safety in mind -- only mainline render their
// results back into the DOM.
//
// For example, SetResource may be run from a helper-thread, but we
// would not want that threaded mutation to propagate instantly back
// into the HTML or CSS DOM. We buffer the changes in the ResoureSlot
// and then render them in the request thread, synchronous to the
// HTML filter execution.
//
// TODO(jmarantz): Add a lock or that we or an overall protocol
// preventing unwanted interference between renderer's reads and
// worker writes.
void SetResource(const ResourcePtr& resource);
// Disables changing the URL of resources (does nothing if slot is not
// associated with a URL (for example, InlineResourceSlot).
void set_preserve_urls(bool x) { preserve_urls_ = x; }
bool preserve_urls() const { return preserve_urls_; }
// If disable_rendering is true, this slot will do nothing on rendering,
// neither changing the URL or deleting any elements. This is intended for
// use of filters which do the entire work in the Context.
void set_disable_rendering(bool x) { disable_rendering_ = x; }
bool disable_rendering() const { return disable_rendering_; }
// Determines whether rendering the slot deletes the HTML Element.
// For example, in the CSS combine filter we want the Render to
// rewrite the first <link href>, but delete all the other <link>s.
//
// Calling RequestDeleteElement() also forces
// set_disable_further_processing(true);
void RequestDeleteElement() {
should_delete_element_ = true;
disable_further_processing_ = true;
}
bool should_delete_element() const { return should_delete_element_; }
// Returns true if any of the contexts touching this slot optimized it
// successfully. This in particular includes the case where a call to
// RewriteContext::Rewrite() on a partition containing this slot returned
// kRewriteOk. Note in particular that was_optimized() does not tell you
// whether *your* filter optimized the slot! For this you should check
// output_partition(n)->optimizable().
bool was_optimized() const { return was_optimized_; }
// Marks the slot as having been optimized.
void set_was_optimized(bool x) { was_optimized_ = x; }
// If disable_further_processing is true, no further filter taking this slot
// as input will run. Note that this affects only HTML rewriting
// (or nested rewrites) since fetch-style rewrites do not share slots
// even when more than one filter was involved. For this to persist properly
// on cache hits it should be set before RewriteDone is called.
// (This also means you should not be using this when partitioning failed).
// Only later filters are affected, not the currently running one.
void set_disable_further_processing(bool x) {
disable_further_processing_ = x;
}
bool disable_further_processing() const {
return disable_further_processing_;
}
// Render is not thread-safe. This must be called from the thread that
// owns the DOM or CSS file. The RewriteContext state machine will only
// call ResourceSlot::Render() on slots that were optimized successfully,
// and whose partitions are safely url_relocatable(). (Note that this is
// different from RewriteContext::Render).
virtual void Render() = 0;
// Called after all contexts have had a chance to Render.
// This is especially useful for cases where Render was never called
// but you want something to be done to all slots.
virtual void Finished() {}
// Update the URL in the slot target without touching the resource. This is
// intended for when we're inlining things as data: URLs and also for placing
// the rewritten version of the URL in the slot. The method returns true if
// it successfully updates the slot target. Resources that are not explicitly
// authorized will get rejected at this point. Note that if you
// call this you should also call set_disable_rendering(true), or otherwise
// the result will be overwritten. Does not alter the URL in any way. Not
// supported on all slot types --- presently only slots representing things
// within CSS and HTML have this operation (others will DCHECK-fail). Must be
// called from within a context's Render() method.
virtual bool DirectSetUrl(const StringPiece& url);
// Returns true if DirectSetUrl is supported by this slot (html and css right
// now).
virtual bool CanDirectSetUrl() { return false; }
// Return the last context to have been added to this slot. Returns NULL
// if no context has been added to the slot so far.
RewriteContext* LastContext() const;
// Adds a new context to this slot.
void AddContext(RewriteContext* context) { contexts_.push_back(context); }
// Detaches a context from the slot. This must be the first or last context
// that was added.
void DetachContext(RewriteContext* context);
// Returns a human-readable description of where this slot occurs, for use
// in log messages.
virtual GoogleString LocationString() const = 0;
// Either relativize the URL or pass it through depending on options set.
// PRECONDITION: url must parse as a valid GoogleUrl.
// TODO(sligocki): Take a GoogleUrl for url?
static GoogleString RelativizeOrPassthrough(const RewriteOptions* options,
StringPiece url,
UrlRelativity url_relativity,
const GoogleUrl& base_url);
protected:
virtual ~ResourceSlot();
REFCOUNT_FRIEND_DECLARATION(ResourceSlot);
private:
ResourcePtr resource_;
bool preserve_urls_;
bool disable_rendering_;
bool should_delete_element_;
bool disable_further_processing_;
bool was_optimized_;
// We track the RewriteContexts that are atempting to rewrite this
// slot, to help us build a dependency graph between ResourceContexts.
VectorDeque<RewriteContext*> contexts_;
DISALLOW_COPY_AND_ASSIGN(ResourceSlot);
};
// A dummy slot used in various cases where Rendering will be performed in
// RewriteContext::Render() instead of ResourceSlot::Render().
class NullResourceSlot : public ResourceSlot {
public:
NullResourceSlot(const ResourcePtr& resource, StringPiece location);
virtual HtmlElement* element() const { return NULL; }
virtual void Render() {}
virtual GoogleString LocationString() const { return location_; }
protected:
REFCOUNT_FRIEND_DECLARATION(NullResourceSlot);
virtual ~NullResourceSlot();
private:
GoogleString location_;
DISALLOW_COPY_AND_ASSIGN(NullResourceSlot);
};
// A resource-slot created for a Fetch has an empty Render method -- Render
// should never be called.
class FetchResourceSlot : public ResourceSlot {
public:
explicit FetchResourceSlot(const ResourcePtr& resource)
: ResourceSlot(resource) {
}
virtual HtmlElement* element() const { return NULL; }
virtual void Render();
virtual GoogleString LocationString() const;
protected:
REFCOUNT_FRIEND_DECLARATION(FetchResourceSlot);
virtual ~FetchResourceSlot();
private:
DISALLOW_COPY_AND_ASSIGN(FetchResourceSlot);
};
class HtmlResourceSlot : public ResourceSlot {
public:
HtmlResourceSlot(const ResourcePtr& resource,
HtmlElement* element,
HtmlElement::Attribute* attribute,
RewriteDriver* driver);
virtual HtmlElement* element() const { return element_; }
HtmlElement::Attribute* attribute() const { return attribute_; }
virtual void Render();
virtual GoogleString LocationString() const;
virtual bool DirectSetUrl(const StringPiece& url);
virtual bool CanDirectSetUrl() { return true; }
// How relative the original URL was. If PreserveUrlRelativity is enabled,
// Render will try to make the final URL just as relative.
UrlRelativity url_relativity() const { return url_relativity_; }
protected:
REFCOUNT_FRIEND_DECLARATION(HtmlResourceSlot);
virtual ~HtmlResourceSlot();
private:
HtmlElement* element_;
HtmlElement::Attribute* attribute_;
RewriteDriver* driver_;
UrlRelativity url_relativity_;
int begin_line_number_;
int end_line_number_;
DISALLOW_COPY_AND_ASSIGN(HtmlResourceSlot);
};
class HtmlResourceSlotComparator {
public:
bool operator()(const HtmlResourceSlotPtr& p,
const HtmlResourceSlotPtr& q) const;
};
typedef std::set<HtmlResourceSlotPtr,
HtmlResourceSlotComparator> HtmlResourceSlotSet;
} // namespace net_instaweb
#endif // NET_INSTAWEB_REWRITER_PUBLIC_RESOURCE_SLOT_H_