Categories
Entertainment

What Does Jelly Roll Think of Bunnie Xo Kissing 24-Year-Old Dylan Wolf at His Bar?

Reading Time: 2 minutes

On the Fourth of July, Bunnie Xo was caught locking lips with Dylan Wolf.

She’s 46 and has scarcely started the divorce process. Also, it was her ex’s bar.

Meanwhile, the reality TV personality is 24.

Plenty of people have opinions. But what does Jelly Roll think?

Bunnie Xo on her podcast on July 10, 2026.
On the Friday, July 10 episode of her ‘Dumb Blonde’ podcast, Bunnie Xo dished on her holiday makeout session nearly one week prior. (Image Credit: YouTube)

He has ‘no issue at all’ with it

Footage of Bunnie Xo’s makeout with Dylan Wolf made headlines after Fourth of July weekend.

A major point that people made when reacting was the location.

Bunnie and Dylan locked lips at Goodnight Nashville.

Jason Bradley DeFord, whom most know as Jelly Roll, owns the bar.

Was Bunnie trying to send a message to DeFord or others? Does he feel some kind of way about the PDA?

According to Bunnie, DeFord “gets it” and actually had “no issue at all” with the steamy kissing session.

“I also have a floor there,” she explained on her Dumb Blonde podcast on Friday, July 10.

“I also feel very safe there. I also feel protected there. That’s our home,” she raved.

“If I’m gonna land at any bar, I’m gonna do it at Goodnight Nashville,” Bunnie added.

Notably, it really sounds almost like a promo for her ex’s bar. Which makes a lot of sense in terms of why he wouldn’t mind. All of this is good publicity.

‘I had fun,’ but …

As for the makeout session, Bunnie explained that she had been on the roof to watch the fireworks.

She explained that she is “living my life out loud and not trying to hide anything” post-split.

Bunnie did acknowledge that she as “learned I cannot do anything out in public like that.”

Not without getting photographed, that is.

Though Bunnie plans to “move forward with a lot more integrity and class,” she shared: “I had fun.”

“I am healing out loud. So what I had fun under the fireworks?” Bunnie reasoned.

She expressed: “I hope I get kissed under 10 more fireworks.” We hope so, too!

Bunnie did clarify that she and Wolf are not dating. And not only because she was already old enough to drink when he was conceived.

“I do not want to be with anybody. There is an icebox where my heart is supposed to be. That s–t is chained up,” she characterized relatably.

Bunnie then affirmed: “Your girl is just out here having fun, living my best life.” Good for her!

What Does Jelly Roll Think of Bunnie Xo Kissing 24-Year-Old Dylan Wolf at His Bar? was originally published on The Hollywood Gossip.

​The Hollywood Gossip

Categories
Entertainment

Mitch McConnell Hauled Onto Ambulance in Chilling Video Prior to Prolonged …

Reading Time: 2 minutes

The prolonged public absence of Mitch McConnell has spawned increasingly credible conspiracy theories that he has died.

Some believe that he may be being kept “alive” via machine to wait out the August 3 deadline for a special election.

His constituents deserve answers. So do the rest of the American people.

But this video of him being loaded into an ambulance and rushed to the hospital is not exactly reassuring.

Mitch McConnell in May 2026.
Sen. Mitch McConnell (R-KY) speaks during a Senate Appropriations Committee hearing in May 2026. (Photo Credit: Chip Somodevilla/Getty Images)

He did not enter the ambulance under his own power

CNN has obtained a video from one of McConnell’s neighbors.

The footage, which dates to Sunday, June 14, shows emergency responders removing the former Senate Majority Leader from his home on a stretcher.

As the video continues, they work with all due haste to place him on the back of the ambulance.

Though McConnell is covered by a blanket and his face does not appear on camera, one bare foot does poke out from beneath the covers.

Two ambulances, a fire truck, and Capitol Police all filled the street, causing the commotion that prompted the neighbor to investigate — and film.

To be clear, the neighbor was not able to confirm with first responders whether or not it was McConnell on the stretcher.

The individual did ask, but received no response.

(It would not be great if Capitol Police identified patients.)

However, the neighbor spoke with an eyewitness who had confirmed seeing McConnell’s face.

Notably, McConnell’s office would go on to issue a statement confirming that he had been admitted to the hospital that morning — without sharing further details of import.

What is this really about?

In the weeks that have followed (more than three, at this point), the claim that McConnell is alive and well has come under a lot of scrutiny.

The 84-year-old politician has not spoken to the public.

Several Republican lawmakers have claimed to have spoken to him, often for extended conversations. However, no credible individuals have confirmed that he is even alive, let alone conscious.

Some believe that he may be brain-dead but on life support. If he remains “alive” (on paper, at least) until August 3, Kentucky can avoid a special election.

Even Kentucky’s governor is asking for real answers.

Critics have accused those responsible for this of elder abuse.

Certainly, it would be cruel to keep someone barely alive simply to avoid a political procedure.

Others have argued that, given his political history, having his barely-not-a-corpse body used as a political prop in order to circumvent the democratic process is exactly what McConnell would have wanted.

Ultimately, however, this isn’t about McConnell. It’s about his constituents in Kentucky. It’s also about the American people, who are missing a Senator.

If McConnell is dead, he leaves a legacy of brutal political strategies that undercut democracy and paved the way for Donald Trump to seize power, however much the two may dislike each other. If he is alive … surely he could just call in to a news network and say hi, right?

Mitch McConnell Hauled Onto Ambulance in Chilling Video Prior to Prolonged … was originally published on The Hollywood Gossip.

​The Hollywood Gossip

Categories
Entertainment

Open Channels FM: The Real Challenge of Technological Solutions: Exclusion in the Age of Verification

David Snead, director of the Secure Hosting Alliance and a long-time Internet policy leader, shares his perspective on the complexities that emerge when technological solutions like age verification are implemented in the digital infrastructure space. Dave’s reflection highlights how the push for more secure, regulated environments can unintentionally create barriers for vulnerable or less tech-savvy […]​WordPress Planet

Categories
Entertainment

Jennifer Pedranti, RHOC Star, Admits Son Started California Wildfire

Reading Time: 2 minutes

Reality stars are used to offering up public confessions about embarrassing parts of their private lives.

But Real Housewives of Orange County star Jennifer Pedranti probably never imagined that she would be offering up such a shocking revelation about her own son.

Pedranti spoke out this week amid reports that linked one of her sons to a Southern California brush fire.

Jennifer Pedranti attends the Universal Horror Unleashed red carpet celebration at Universal Horror Unleashed on September 18, 2025 in Las Vegas, Nevada.
Jennifer Pedranti attends the Universal Horror Unleashed red carpet celebration at Universal Horror Unleashed on September 18, 2025 in Las Vegas, Nevada. (Photo by Greg Doherty/Getty Images for Universal Horror Unleashed)

The Bravo vet confirmed Thursday that one of her children was involved in the incident, though she stressed that the family is taking the situation very seriously.

“I would like to clear up the rumblings of my son having involvement in the Ladera Ranch fire,” Jennifer wrote on her Instagram Stories.

“He and others were involved.”

Jennifer, who shares five children with ex-husband William Pedranti, did not reveal which of her four sons was involved.

“My ex husband and myself take this very serious,” she continued.

“This behavior is unacceptable and we are deeply sorry to our community. This is a tough learning moment for our son and our family.”

Jennifer also praised first responders for preventing the situation from becoming much worse.

“We were so thankful there was no property damage and nobody was hurt. The fire and police were absolutely amazing and gave these boys a lesson they will never forget.”

According to Jennifer, her son was not arrested but will still face consequences at home.

“He will be taking a fire safety course to make sure a mistake like this never happens again,” she explained. “There are consequences within our family and we will make sure our son learns from this accident.”

The brush fire broke out Tuesday afternoon in Ladera Ranch, California, eventually burning roughly seven acres.

Firefighters mounted an aggressive response from both the ground and the air, successfully containing the blaze before it could spread to nearby homes.

Officials reported no injuries and no property damage. Authorities also confirmed that no arrests were made, though the cause of the fire remains under investigation.

Jennifer, who joined The Real Housewives of Orange County as a full-time cast member in Season 17, has largely kept her family life out of the spotlight.

But with Season 20 now underway, the reality star chose to address the incident head-on, acknowledging her son’s involvement while making it clear her family intends to treat the matter as a serious lesson rather than simply moving on.

Southern California was ravaged by wildfires in 2025, and millions of residents who lost their homes are still struggling to rebuild their lives.

So while the actions of Jennifer’s son might be shockingly ignorant, hopefully her fellow SoCal residents can find solace in the fact that she’s taking the situation very seriously.

We will have further updates on this developing story as new information becomes available.

Jennifer Pedranti, RHOC Star, Admits Son Started California Wildfire was originally published on The Hollywood Gossip.

​The Hollywood Gossip

Categories
Entertainment

Otter Blocks 3.2.0: AI Page Building and a New Design Library

Otter Blocks 3.2.0 is now live, bringing AI that builds full sections and pages, a redesigned AI writing toolbar, AI form autoresponders, a completely rebuilt Design Library, a new Content Slider block, more reliable forms, and full WordPress 7.0 support. This release focuses on the slowest part of building in the block editor: getting started….

The post Otter Blocks 3.2.0: AI Page Building and a New Design Library appeared first on Themeisle Blog.

​Themeisle Blog

Categories
Entertainment

Gigi Hadid Shares Glimpse at Summer With Bradley Cooper in Rare Photos

Gigi Hadid Bradley CooperGigi Hadid and Bradley Cooper have been hitting the runways.
Indeed, the model and the Silver Linings Playbook star are no stranger to the tarmac, jetsetting from New York City to Paris in one…
​E! Online (US) – Top Stories

Categories
Entertainment

How to Connect AI Agents With WordPress using MCP (Step by Step)

AI assistants like Claude Code, Cowork, and ChatGPT are incredible productivity boosters, and if you wished that you could connect these AI tools with WordPress directly, then you’re not alone.

Lately, I have been using WordPress MCP by WPVibe to let my AI assistant manage my website, and it’s truly amazing how much time this saves. You can simply ask it to create a post, upload image, handle admin tasks, and more from a single conversational prompt.

In this step by step tutorial, I’ll show you how to connect your favorite AI tools with WordPress using MCP along with sharing a few work examples, so you can see what becomes possible when you combine WordPress + AI.

How to Connect AI Agents With WordPress Using MCP

You can use the quick links below to jump to any section:

What is a WordPress MCP and What Can It Do?

MCP stands for Model Context Protocol. It is as an open standard that lets AI assistants connect to outside tools and services.

Think of it as a universal adapter. Instead of every AI tool building a custom integration with every service, they all speak the same language, so any tool and service that support MCP can work together.

With a WordPress MCP, your AI assistant can see what your website supports and carry out tasks from a plain-text prompt. It does this using a connection WordPress already has built in (the REST API), so there’s nothing extra to install.

You can use WordPress MCP to manage your WordPress site and perform tasks including:

  • Draft and publish posts – Create blog posts as drafts, set titles, add categories and tags, and publish when you’re ready.
  • Upload media – Pull images from any public URL directly into your WordPress media library.
  • Manage categories and tags – Create or rename taxonomy terms and assign them to posts.
  • Run admin tasks – Flush your site cache, check which plugins are active, and activate or deactivate plugins.
  • Use plugin abilities – On WordPress 6.9+, many plugins register their own actions the AI can discover and run automatically

Each of these can be done from a plain-text prompt from your favorite AI tool like Claude Code, Cursor, ChatGPT, etc. I’ll show you the exact prompts once you’re fully set up.

What You Need Before Getting Started

  • Self-hosted WordPress 6.9 or later – With the REST API enabled (it’s on by default). You need version 6.9 or later for plugin abilities API which is allows you to use AI to manage plugins like AIOSEO, WPForms, etc.
  • A publicly accessible site – Your site must be reachable on the internet because local development sites won’t work unless exposed via a tunnel.
  • A free WPVibe account — You’ll create this during setup.
  • An HTTPS-enabled site — WordPress application passwords require SSL and they won’t function on http:// sites. See our guide on how to add SSL and HTTPS to WordPress.

Step 1. Set Up MCP on Your WordPress Website

The easiest way to add MCP to a self-hosted WordPress site is with WPVibe.ai. It’s free and runs on a hosted server, so there’s no infrastructure to configure. It also works with every major AI tool through a single setup.

You can read our full WPVibe review for a deeper look at everything it can do, but this guide covers what you need to get connected.

Other ways to set this up: There’s also an official WordPress MCP Adapter, which pairs with the new Abilities API in WordPress 6.9, but it’s built for developers and needs manual configuration.

Install and Activate the Vibe AI Plugin

First, you will need to install and activate the free WPVibe plugin. If you need any help, our guide on how to install a WordPress plugin covers each step.

Along with connecting your AI tools, the WPVibe plugin unlocks WP-CLI commands, theme file editing, and the plugin abilities that I talk about later in this guide.

Once activated, go to Vibe AI » Vibe AI in your WordPress dashboard. You’ll see the MCP server URL and a three-step setup guide.

Keep this tab open. You’ll need the URL in the next step.

The Vibe AI plugin admin page in wp-admin showing the three-step connection setup

Step 2. Connect Your AI Tools to WordPress

With WPVibe set up, connecting your AI tool takes under a minute. You add the same server URL to any AI client you use: https://mcp.wpvibe.ai/mcp.

You’ll find instructions on how to do this in the official WPVibe documentation. But let me show you exactly where to find that setting in some popular AI platforms.

Connecting Claude

If you’re just getting started, I recommend beginning with Claude.

The simplest method is to add the WPVibe URL once via Claude.ai on the web, and it syncs automatically to Claude Desktop, Claude Code, and the Claude mobile apps with no separate setup needed.

Note: On Team and Enterprise workspaces, only an Owner or Admin can add connectors. Individual members on those plans can authenticate with WPVibe once the admin has added it, or use the Claude Code method in step 3 below.

  1. Free, Pro, and Max plans: In Claude.ai, go to Customize » Connectors. Click the + button, select ‘Add custom connector’, and paste https://mcp.wpvibe.ai/mcp.
  2. Team and Enterprise (admin only): Go to Organization settings » Connectors. Click ‘Add’, select Custom » Web, and paste the WPVibe URL.
  3. Claude Code (any plan): In your terminal, run claude mcp add --scope user wpvibe --transport http https://mcp.wpvibe.ai/mcp. Then, open Claude Code, type /mcp, select wpvibe, and choose ‘Authenticate’.

Once saved, follow the on-screen prompt to authorize your WordPress site.

Connecting Claude.ai (Web) to WPVibe
Connecting ChatGPT

WPVibe is available directly in the ChatGPT App Marketplace, so both free and paid users can connect without copying server URLs or editing config files.

In ChatGPT, click ‘Apps’ in the sidebar and search for WPVibe. Then click ‘Connect’ on the app page and sign in with your WPVibe account when prompted.

After connecting, ChatGPT pre-fills ‘@WPVibe’ at the start of each message. You can delete it with backspace for prompts unrelated to WordPress. Leave it in place when following the authorization step below.

Connecting ChatGPT to WPVibe
Connecting Cursor

Cursor adds MCP servers through a JSON config file. In Cursor, go to Settings » MCP and click the ‘Add new global MCP server’ button. This opens the mcp.json file.

Add the following entry and save:

{
  "mcpServers": {
    "wpvibe": {
      "url": "https://mcp.wpvibe.ai/mcp"
    }
  }
}

Make sure you restart Cursor after saving.

WPVibe will appear in the MCP list once it connects.

Cursor Settings showing the MCP panel with an option to add a new global MCP server
Connecting Windsurf

In Windsurf, open the Cascade panel and click the ‘Plugins’ icon (puzzle piece). Search for WPVibe, click ‘Enable’, and complete the sign-in flow when prompted.

To configure manually instead, edit ~/.codeium/windsurf/mcp_config.json and add the following, then restart Windsurf:

{
  "mcpServers": {
    "wpvibe": {
      "serverUrl": "https://mcp.wpvibe.ai/mcp"
    }
  }
}

Note: Windsurf uses serverUrl (not url) in its config file. Using the wrong key will cause the connection to fail.

Authorize Your WordPress Site

With the MCP URL added to your AI client, go back to Vibe AI » Vibe AI in your WordPress dashboard.

Under step 3, you’ll find a ready-to-copy prompt with your site’s URL already filled in.

The WPVibe Setup Page

Paste that prompt into your AI chat. Your AI assistant will call WPVibe and return a one-click authorization link. Simply click it to approve the connection.

You never see or copy a password. WordPress and WPVibe handle the credentials securely in the background.

WPVibe site showing connected status in the Vibe AI plugin admin after authorization

What You Can Do Once You’re Connected (Worked Examples)

Once your AI tool is connected to WordPress, here are the first prompts I tried, including one for WooCommerce store owners.

My examples are from Claude Code terminal, but these will work with whichever AI tool you connected.

1. Write and Draft a Blog Post

Type this into your AI tool: ‘Create a draft WordPress post titled “How to Start a Blog” with an intro paragraph explaining why blogging is still worth it.’

AI Prompt for Drafting a New Post

Your AI assistant calls the WordPress REST API, saves the post as a draft, and returns a confirmation with a link to edit it in wp-admin.

Nothing goes live until you choose to publish it yourself.

A Draft Post Created Using AI
2. Upload a Photo to Your Media Library

Type: ‘Upload this image to my WordPress media library: [paste a public image URL]. Set the alt text to “A person working at a laptop”.’

AI Prompt for Adding a Public Image to Your WordPress Media Library

WPVibe validates the source URL, downloads the image, and adds it to your media library with the alt text you specified.

You can reference it in any post from there.

WordPress media library showing a newly uploaded image via MCP

Note: The prompt above uses a public URL, which is the simplest approach. Uploading a local file from your computer works too, but WPVibe will generate a browser upload link as an intermediate step.

3. Manage Your WooCommerce Store

WooCommerce 10.9 ships with native MCP support, which exposes product and order abilities through the same standard that WPVibe uses.

If your site runs WooCommerce, try: ‘Draft a new product called “Summer T-Shirt” with a short description and a price of $29.95.’

AI Prompt for Creating a New WooCommerce Product

The product saves as a draft in your store. Beyond creating products, your AI can also query orders, update their status, and add order notes, so you can handle routine store admin from the same chat window.

You can see the example below.

A New Product Created by AI and WPVibe

The next three examples go beyond WordPress’s built-in actions. On WordPress 6.9 or later, plugins can register their own actions (called ‘abilities’) that your AI discovers automatically, so if you use these tools, you can drive them from the same chat.

4. Optimize Your SEO with AI

If you use AIOSEO, then you can ask your AI to handle common SEO tasks from the chat window. It can check TruSEO scores, generate meta titles and descriptions, and run SEO audits.

My starting point was to ask it to find every post missing a meta description and write one for each.

Try this prompt: ‘Find all posts missing meta descriptions and write and apply them in one go.’

Claude Code chat showing prompt to find and apply missing AIOSEO meta descriptions

Your AI checks every post, writes the missing meta descriptions, and applies them through AIOSEO in one step.

See our complete AIOSEO setup guide to get it configured first.

AIOSEO meta description field in WordPress showing an AI-generated meta description
5. Manage Landing Pages and Maintenance Mode

If you use SeedProd, then your AI can manage your coming soon page, maintenance mode, and landing page settings without going into wp-admin.

For example: ‘Check if maintenance mode is currently active’ or ‘Enable the coming soon page.’

6. Build a Form with AI

If you use WPForms, then your AI can build forms from a plain-English description.

Try: ‘Create a contact form with name, email, and a message field.’ WPForms creates the form in your dashboard, ready to configure and embed.

Before using this, make sure you enable write access under WPForms » Tools.

Enabling MCP Write Access for WPForms

WPForms Lite supports basic field types. Paid plans add phone, date/time, file upload, and more.

See What Your Site Can Do

You don’t have to guess which of your plugins support this. Because WPVibe automatically discovers every registered ability, you can just ask your connected AI: ‘What can you do on my WordPress site?’

It will list the abilities your installed plugins expose, so the answer stays accurate no matter which plugins you run.

Is WordPress MCP Safe?

I had the same concern when I first tested this. Here’s how WPVibe handles security.

WordPress manages the connection using application passwords, a built-in feature that creates a separate password for each external tool you connect. The AI only gets the permissions of the user account you connected.

So, it’s worth connecting with a limited, non-admin account rather than your main administrator login. If you create a dedicated Editor-role user for the connection, then the AI can only do what an Editor can do.

WPVibe encrypts your application password before storing it, using a separate key for each site, and keeps it on secure servers hosted by Cloudflare. Every connection between your AI client, WPVibe, and your WordPress site is encrypted too, so your credentials are never exposed along the way.

WPVibe also doesn’t store your conversation content. It stays within your AI client.

On the WordPress side, new posts always save as drafts and deletions go to the trash rather than permanent deletion.

To fully revoke access at any time, go to Users » Profile in your WordPress dashboard and scroll down to the Application Passwords section to delete it.

Troubleshooting Connection Issues

Connecting your AI client to WordPress is the step that trips people up most often.

If the connection fails or WPVibe doesn’t appear, then work through these checks:

  • WPVibe isn’t in your client’s tool or MCP list – Restart the AI client after saving the config. New MCP servers only load on a fresh start.
  • The connection fails silently in Windsurf – Windsurf uses the serverUrl key, not url. The wrong key produces no error message, so double-check it.
  • Application passwords won’t authenticate – Your site must run on HTTPS. Application passwords don’t work over http://.
  • Your AI client can’t reach the site – The site has to be publicly reachable. A local development site won’t connect unless you expose it through a tunnel.
  • Authorization keeps failing – Re-check that you pasted the correct MCP server URL: https://mcp.wpvibe.ai/mcp.

Frequently Asked Questions About WordPress MCP

Is WPVibe free?

Yes. WPVibe has no API key costs and everything in this guide works on the free plan. There is now a paid Pro plan (early-access pricing, $99 per year) that adds higher daily usage limits and priority support, not new features, so you don’t need it for anything covered here.

Does WordPress MCP work with all AI tools?

WPVibe works with Claude.ai (web), Claude Desktop, Claude Code, ChatGPT, Cursor, and Windsurf. Because MCP is an open protocol, any new AI tool that adopts the standard will work with the same WPVibe setup automatically.

Does WordPress MCP work with WordPress.com?

No, not through WPVibe. WordPress.com has its own built-in MCP setup that works differently. This guide covers self-hosted WordPress (WordPress.org) only.

Do I need WordPress 6.9 to use MCP?

No. You can create posts, upload media, and run admin tasks on WordPress 6.0 or later. WordPress 6.9 is only required if you want to use plugin abilities like those from All in One SEO or SeedProd.

Can I connect more than one WordPress site to WPVibe?

Yes. WPVibe supports multiple sites under one free account. Install the Vibe AI plugin on each additional site and complete the site authorization step. Your existing WPVibe account covers all your sites.

What happens if I disconnect WPVibe from my site?

You can remove a site from WPVibe in the plugin admin or on wpvibe.ai at any time. To fully revoke access, also delete the credential under Users » Profile » Application Passwords.

I hope this article helped you get WordPress MCP set up on your site. You may also want to see our guide on the best AI Automation Tools for WordPress.

If you liked this article, then please subscribe to our YouTube Channel for WordPress video tutorials. You can also find us on Twitter and Facebook.

The post How to Connect AI Agents With WordPress using MCP (Step by Step) first appeared on WPBeginner.

​WPBeginner

Categories
Entertainment

RHOC’s Jennifer Pedranti Says Son Was Involved in Starting 7-Acre Fire

Jennifer PedrantiJennifer Pedranti’s son is learning a valuable lesson.
The Real Housewives of Orange County star—who shares kids Harrison, 22, Dawson, 19, Greyson, 16, Everleigh, 14, and Dominic, 12, with…
​E! Online (US) – Top Stories

Categories
Entertainment

For The Crispiest Fried Chicken Breast Grab This Flour

Not all flours are created the same, and this one particular brand is highly regarded for making the crispiest and crunchiest fried chicken coating.

​Mashed – Fast Food, Celebrity Chefs, Grocery, Reviews

Categories
Entertainment

Dennis Snell: See DATA, CDATA, RCDATA, and PCDATA oh my!

HTML and XML are markup languages based on plaintext files. This means that any given character could be part of a syntax form (a tag, a comment, a character reference, etc…) or it could be representing itself the way it reads in the file literally.

<tag>&middot; Text node</tag>

Whenever a character might be ambiguous, both languages require explicit indication of the intent of the character. In HTML this occurs via escaping, while XML allows escaping or wrapping the content in a marked section, specifically a CDATA section.

&lt;tag&gt;
<![CDATA[<tag>· Text node</tag>]]>

These terms confuse me at times, especially since CDATA and CDATA sections are distinct forms of the same content, and it’s easy to conflate each term. This post is here to disambiguate the terms, their meanings, and why they exist.

The punchline comes at the end, but the story is hopefully worth the read.

Markup and mixed content

One of the first jobs of a parser for any plaintext-oriented format is to determine if the next input character represents real text or is part of a syntax form that carries special meaning. If it’s a syntax form we would call it markup, but if the characters are part of real text meant for display or rendering or reading then we call it data.

Anything that is not syntax is data.

The interpretation of the next character depends on the region of the document in which it’s parsed. While the rules for syntax forms are complicated1, this post will focus on the data forms.

PCDATA — “parsed character data”

May form: tags, comments, sections, character references, literal text.

Characters in this region could be data or could form the start of a new markup element. It’s “parsed” because it needs parsing before determining what it represents.

The HTML specification renames this to Data, which is simpler and a bit harder to search for. In XML, however, it’s used in a document-type definition (DTD). When an element may contain content — text — its data model must include #PCDATA. Otherwise the only characters allowable within that element are other elements, comments, and whitespace. XML documents are required to be valid SGML documents, so its own specification adopts the terminology from SGML’s.

Those who have worked with DTDs might note that elements in XML may contain #PCDATA while attributes contain CDATA instead. First of all, the # is there only to make it explicit that PCDATA is referring to the reserved keyword, rather than a <pcdata> element. Secondly, there’s a good reason for this, which is that attributes can only contain text — they can’t contain other elements of markup. If an attribute value could contain a <span> element, for example, then the attribute value would need to be #PCDATA instead, but this is prevented by design.

PCDATA actually contains more than just literal text and elements. In addition to comments, processing instructions, and other node-like syntax, one important feature of PCDATA is the character reference. These make it possible to represent characters that would conflate with syntax (such as ‘<’ — &lt;) or which might be cumbersome to enter on a keyboard (such as ‘§’ — &sect;). When parsing, each character in these sequences neither creates an element nor displays as the text itself; rather, the entire sequence is parsed and translates into the character it refers to.

HTML pre-specifies a fixed set of named character references, but any Unicode code point may be referenced by its decimal or hexadecimal numeric index. While XML also allows referencing code points by their index2, it only pre-specifies the five named characters which correspond to its main markup introducers: <, >, &, ', and ". In XML, any additional named character references are created through the DTD by defining entities.

CDATA — “character data”

May form: [character references], literal text.

If a character isn’t markup, then it’s character data, which means that it’s representing its literal self or it’s part of a character reference. Once the parser has entered this region it will not create markup elements.

CDATA is the most confusable kind of character data; this is because there are many kinds of CDATA that share the same name:

  • XML attributes may contain CDATA, where character references are decoded.
  • XML CDATA sections only contain CDATA, but character references are not decoded.
  • HTML kind of has the same CDATA sections, but only in foreign elements (inlined SVG and MathML elements).
  • SGML elements may be declared to have a CDATA content model, in which case all content until the appropriate closing tag is to be parsed as character data, where character references are not decoded.

CDATA sections contain only literal text

Many people are familiar with CDATA sections, but it took me far longer to understand them than my intuition led on. They are the vestige of SGML “marked regions” which tell the parser to handle a specific range of bytes in a special way. The CDATA section is one of those, which tells the parser to completely turn off until it reaches ]]>.

<![CDATA[literal characters only in here]]>

It had other marked sections, however, which served different purposes.

<![IGNORE[everything in here is ignored; it doesn’t exist.]]>
<![INCLUDE[in here things <em>do</em> exist as normal.]]>
<![RCDATA[read on to learn about RCDATA!]]>

The IGNORE and INCLUDE sections may seem strange, since SGML already has comments, and INCLUDE effectively does nothing, but the sections can be marked by replaced entities, making for conditional inclusion which can be overwritten via command-line arguments when invoking the SGML parser.

<!ENTITY % review-only "IGNORE">
...
<![%review-only;[
<aside>
Add `-Dreview-only=INCLUDE` when building drafts.
This note won’t appear otherwise.
</aside>
]]>

XML only retained CDATA sections from SGML, while HTML never included them. They are useful because they are so easy to parse. All characters inside of them are to be treated as literal text, up until the first occurrence of the terminating ]]>. Unlike elements, the marked sections do not nest.

There are no CDATA sections in HTML

The Internet is full of discussions about the use of CDATA sections in HTML, but there are no such things, mostly. HTML itself is an amalgam of pure HTML and embedded SVG and MathML. Content inside of those embedded SVG and MathML elements is parsed differently, and within this “foreign content” there are CDATA section nodes.

When something which look like a CDATA section appears in an HTML document, it’s transformed into a “bogus” HTML comment and considered a snippet of malformed markup. To make things more confusing, the parsing rules differ inside an HTML document for these regions depending on whether they are found within HTML elements or foreign elements.

  • When a real CDATA section appears within SVG and MathML, it parses as in XML or SGML — everything is literal text until the nearest ]]>.
  • When a malformed CDATA look-alike appears in an HTML element, it gets special treatment — the parser only turns off until the nearest >. This means that these sections end even without a closing ]]>, and when they do, all of their contained content disappears from the page.

That small difference confuses naïve parsers and is a regular source of bugs.

<div><![CDATA[There are no tags in here.]]></div>
<svg><text><![CDATA[<none> here either.]]></text></svg>
<div><![CDATA[But there <em>are</em> tags in here]]></div>
the section ends here ╯ ╰ start of a real end tag
The following is the equivalent markup to the third line.
<div><!--But there <em-->are</em> tags in here]]></div>

SGML contains CDATA regions outside of marked CDATA sections

SGML made it possible to define more kinds of content than XML does for a given element. For example, an element in SGML can be declared to have a CDATA content model, in which case the element itself behaves like a CDATA section. All characters after the opening tag are treated as literal text until the parser finds the nearest appropriate end tag3. XML rejected this ability because it increases the complexity of the parser and requires that every document also contains a full DTD when parsing. For example, if an element were declared to have CDATA content, then a <at> b would represent that literal string; on the other hand, if it were declared like any other normal element, it would have three children: “a ”, the <at> opening tag, and “ b”.

<!ELEMENT verbatim - - CDATA>
...
<verbatim>
There are <no> tags in here, because this is CDATA,
but you wouldn’t know without reading the DTD,
overcomplicating the demands on the parser.
</verbatim>

These kinds of elements do exist in HTML, though a few were modified when HTML5 was standardized in 2008. Inside of the elements, the parser essentially turns off, which makes them easy to parse and can help avoid the need to extensively escape content. These elements are, of course, <script> and <style>4.

Were it not for the CDATA declared content model, every angle bracket and ampersand would have to be escaped in included JavaScript and CSS. In XHTML this was required, because it had no CDATA declared content model (since it was XML)5.

All text in XML is CDATA

Herein lies the most-confusing aspect of discussing CDATA — XML contains CDATA sections as well as CDATA as normal text. After parsing there is no distinction between &lt;tag&gt; and <![CDATA[<tag>]]> in the parsed content.

Many XML generators (or serializers) provide two mechanisms for creating text content: one wraps text in a CDATA section and leaves the text as it came (apart from avoiding including the terminating sequence); the other escapes syntax characters instead. While there are times where it would be appropriate to intentionally pick one over the other, a good library design would at least offer a third mechanism (if not only providing this third mechanism) which simply produces CDATA, itself determining when to wrap and when to escape6, and whether or not to produce chunks of wrapped text interspersed with chunks of escaped text.

The real difference between these two kinds of CDATA is purely presentational in the source document, as the XML snippet below only contains one text node, not two. Creating CDATA does not imply creating a CDATA section!

<rule><![CDATA[#X13<d&r>]]> (&pp;4 &ss;3.11)</rule>

RCDATA — “replaceable character data”

May form: character references, literal text.

There’s one more confusing designation for characters in the HTML and XML input streams: RCDATA. RCDATA is almost identical to CDATA, except that in contexts where CDATA does not decode character references and entities, RCDATA will decode them into CDATA. This is confusing, because in the context of an XML attribute, the CDATA designation in a DTD automatically implies that character references are decoded, unlike the CDATA sections in content.

To this end there are no RCDATA attributes, since character references are always decoded inside attribute values. The RCDATA declaration is like the SGML CDATA content declaration: all characters following the opening tag for this element will be treated as text until the nearest matching closing tag (the difference being only that character references are recognized and decoded).

It’s worth remembering that XML rejected the CDATA content type because of how it complicates parsing, and it also rejected the RCDATA type. On the other hand, RCDATA was incorporated into HTML, but statically so. HTML has no configurable DTD, but in its specification two elements contain RCDATA content:

  • TITLE
  • TEXTAREA

While it’s easy to comprehend the way that <textarea> works, and that’s probably because we are used to entering text into one on a web page, the behavior of <title> is consistently confused in all manner of programming languages, platforms, and HTML-parsing code.

The TITLE element only contains character data — it cannot contain other markup. The parsing is among the easiest sections of an HTML document to parse: once the <title> opening tag is detected, the parser can capture everything until the nearest </title> closing tag. Everything it captured is literal text, after decoding character references.

<!-- the title is "<title>" -->
<title><title></title>
<!-- equivalent HTML -->
<title>&lt;title&gt;</title>

This complicates content management systems like WordPress which allow posts to have HTML in their post titles, because a page can show richly-formatted article titles which cannot be represented in the browser tab’s label, and care must be taken to extract the plaintext content from that HTML before display in those contexts.

Coda

HTML and XML both speak about different kinds of characters in their source documents and content models, which traces from the complicated ways that SGML documents could be constructed. SGML’s complexity almost always stems from the central idea that computers should do extra work to remove the hassle for humans to enter structured content in plaintext documents.

HTML, inspired by SGML, adopted some of the names and mechanisms for parsing those regions of text in distinct ways, but codified a single parsing standard independent of SGML. When XML was later developed, it was meant to form a simplified subset of SGML. This subset flipped the tradeoffs, leaning on humans performing extra work to remove the hassle for computers to parse structure in plaintext documents. For these text forms, this meant rejecting a few of the constructs while retaining others.

This is also another demonstration of how balanced tags are not enough to have well-behaved HTML with a naïve parser. A well-formed XML document may be parsed with a terse PERL script and regular expression, but HTML relies heavily on the context in which characters are found. Any HTML parser must know the special rules for each kind of element’s content model.

In summary

  • When it’s unclear whether a character forms text or markup, that is PCDATA. Once parsed, there is no PCDATA anymore; it’s either a form of DATA or MARKUP.
  • All text nodes in HTML are “DATA.”
  • “CDATA” just means “character data” and means that after parsing, the content is text. It does not indicate whether character references are to be decoded or not; that comes from the region in the document, based on its context.
  • There are no CDATA sections in HTML7.
  • All text nodes in XML are CDATA, but only after being parsed.
  • CDATA sections offer a convenient way to avoid escaping, but are indistinguishable from the equivalent escaped text.
  • HTML contains two special RCDATA elements which only and always contain a single text node child: <title> and <textarea>. Everything until the closing tag will be parsed as text, even if it looks like markup.

This post is already long and still over-simplifies the picture. SGML is a rich and robust specification and includes NDATA and SDATA, HTML includes a latching PLAINTEXT parsing mode in which the rest of the entire document is parsed as literal character data, and there are other surprising goodies in how entities interact with the character mode.

Thanks for making it through to the end, or jumping directly here if you couldn’t wait.

  1. As an example, each part of a tag — its name, attribute names, attribute values — carries its own parsing rules. The same is true for comments, DOCTYPE declarations, and every other syntax form. ↩
  2. XML only allows character references to the characters in its “character set,” which is almost all Unicode code points, but excludes some control characters and U+FFFE and U+FFFF. ↩
  3. Because SGML was designed to minimize the amount of necessary syntax, it’s not necessary to have a full end tag for an open element, but that’s a simple-enough model to understand the concept. ↩
  4. The <style> element is straightforward, but the <script> element has its own complicated modification of the CDATA content model. It’s mostly CDATA, but makes it possible to escape the closing tag so that very old pages won’t break. HTML also applies this parsing mode for the <iframe>, <noembed>, <noframes>, and <noscript> elements (as well as for the deprecated <xmp> element), but these nominally should have no content inside of them (or shouldn’t be used); applying the CDATA content model prevents creating other elements as their children. ↩
  5. Frustratingly, in XHTML one must escape JavaScript and CSS in the page to avoid parsing failure, while in HTML one must not. This alone makes for a complicated stage in any reliable HTML/XHTML converter. ↩
  6. Wrapping a language like HTML inside a CDATA section is a convenient way to represent the HTML visually and retain the ability to easily modify it, but entities present a problem. The serializer must either pre-translate the entity into its resolved character content, losing the macro-like behavior and its name; or leave the entity in place, thus nullifying it because it will not be recognized as an entity on parse. However, in such a situation, a serializer is free to terminate the CDATA section, append the entity, and open a new one to continue. ↩
  7. As mentioned in the discussion about CDATA, embedded SVG and MathML elements can contain CDATA sections, but these are not technically HTML elements. ↩

​WordPress Planet